När du uppdaterar på det sätt du gör måste du hämta dokumentinnehållet för att kunna inspektera det och göra sådana ändringar. MongoDB har inga atomoperationer som verkar på befintliga värden på det sätt som du vill göra, så iteration krävs naturligtvis.
Det finns ingen verklig skillnad i "fråga"-delen av hur du matchar det reguljära uttrycket mellan dina två versioner av uttalandet. Oavsett vad, konverteras innehållet till BSON innan det skickas till servern ändå, så om du använder en standarduttrycksbyggare eller ett direkt BSON-dokument har liten betydelse.
Men vidare till de prestandaförbättringar som kan göras.
Använd Bulk Operations för att uppdatera
Som nämnts är Bulk Operations sättet du bör uppdatera på en sådan listiteration, och du "bör" också använda en markör istället för att konvertera alla resultat till en lista, eftersom det sparar minne.
Undviker alla specifika typdeklarationer och representerar bara som BsonDocument
(vilket förmodligen kommer att spara dig på rangering, men det behövs inte) då skulle den grundläggande exempelprocessen vara:
var pattern = @"(?si)<([^\s<]*workUnit[^\s<]*)>.*?</\1>";
var filter = Builders<JobInfoRecord>.Filter.Regex(x => x.SerializedBackgroundJobInfo,
new BsonRegularExpression(pattern, "i"));
var ops = new List<WriteModel<BsonDocument>>();
var writeOptions = new BulkWriteOptions() { IsOrdered = false };
using ( var cursor = await records.FindAsync<BsonDocument>(filter))
{
while ( await cursor.MoveNextAsync())
{
foreach( var doc in cursor.Current )
{
// Replace inspected value
var updatedJobInfo = Regex.Replace(doc.SerializedBackgroundJobInfo, pattern, "<$1></$1>");
// Add WriteModel to list
ops.Add(
new UpdateOneModel<BsonDocument>(
Builders<BsonDocument>.Filter.Eq("JobTypeValue", doc.JobTypeValue),
Builders<BsonDocument>.Update.Set("SerializedBackgroundJobInfo", updatedJobInfo)
)
);
// Execute once in every 1000 and clear list
if (ops.Count == 1000)
{
BulkWriteResult<BsonDocument> result = await records.BulkWriteAsync(ops,writeOptions);
ops = new List<WriteModel<BsonDocument>>();
}
}
}
// Clear any remaining
if (ops.Count > 0 )
{
BulkWriteResult<BsonDocument> result = await records.BulkWriteAsync(ops,writeOptions);
}
}
Så istället för att göra en begäran till databasen för varje enskilt dokument som hämtas från frågan, skapar du en List
av WriteModel
operationer istället.
När den här listan har vuxit till ett rimligt värde ( 1000 i det här exemplet ) begår du skrivoperationen till servern i en enda begäran och ett svar för alla batchoperationer. Här använder vi BulkWriteAsync
.
Du kan skapa satserna i en storlek större än 1000 om du vill, men det är i allmänhet ett rimligt antal att hantera. Den enda riktiga hårda gränsen är BSON-gränsen på 16MB, vilket eftersom alla förfrågningar fortfarande faktiskt är BSON-dokument gäller detta fortfarande. Hur som helst krävs det många förfrågningar för att närma sig 16 MB, men det finns också en impedansmatchning att överväga i hur förfrågan kommer att behandlas när den faktiskt når servern, som dokumenterats:
"Varje grupp av operationer kan ha högst 1000 operationer. Om en grupp överskrider denna gräns kommer MongoDB att dela upp gruppen i mindre grupper om 1000 eller färre. Till exempel, om listan med massoperationer består av 2000 infogningsoperationer, MongoDB skapar 2 grupper, var och en med 1000 operationer."
Genom att hålla storleken på begäran på samma nivå som hur servern kommer att bearbeta den, får du också fördelen av yield
där "flera batcher" faktiskt kan agera i parallella anslutningar till servern, snarare än att låta servern göra uppdelningen och köandet.
Det returnerade resultatet är BulkWriteResult
som kommer att innehålla information om antalet "matchningar" och "modifieringar" etc från den sats av operationer som skickas.
Eftersom operationerna är i "batcher" är det naturligtvis vettigt att sedan kontrollera i slutet av loop-iterationen för att se om det finns några fler "batchade" operationer i listan, och sedan naturligtvis skicka in på samma sätt.
Notera även IsOrdered = false
som BulkWriteOptions
innebär att satsen av operationer faktiskt inte exekveras i seriell ordning, vilket innebär att servern faktiskt kan köra taks "parallellt". Detta kan göra "stora" hastighetsförbättringar där ordningen på engagemanget inte krävs. Standard är att skicka "beställt" och seriellt.
Detta krävs inte för att ställa in det här alternativet, men om din beställning inte är viktig (vilket det inte borde vara i det här fallet eftersom inga andra åtgärdsbegäranden här beror på föregående ändring av ett dokument) så är förbättringen du får värt besväret.
Vad det här handlar om är att "minska" antalet faktiska förfrågningar som görs till servern. Att skicka uppdateringar och invänta svar tar tid, och i stora verksamheter är det en mycket kostsam övning. Det är vad Bulk Operations är tänkta att hantera, genom att tillämpa flera operationer inom en begäran.
Att minska den omkostnaden är en "stor" prestandavinst. Det är därför du använder det här.