Andre
Andre

Reputation: 4625

MongoDB update array of documents and replace by an array of replacement documents

Is it possible to bulk update (upsert) an array of documents with MongoDB by an array of replacement fields (documents)?

Basically to get rid of the for loop in this pseudo code example:

for user in users {
  db.users.replaceOne(
      { "name" : user.name },
      user,
      { "upsert": true }
}

The updateMany documentation only documents the following case where all documents are being updated in the same fashion:

db.collection.updateMany(
   <query>,
   { $set: { status: "D" }, $inc: { quantity: 2 } },
   ...
)

I am trying to update (upsert) an array of documents where each document has it's own set of replacement fields:

updateOptions := options.UpdateOptions{}
updateOptions.SetUpsert(true)
updateOptions.SetBypassDocumentValidation(false)
_, error := collection.Col.UpdateMany(ctx, bson.M{"name": bson.M{"$in": names}}, bson.M{"$set": users}, &updateOptions)

Where users is an array of documents:

[
{ "name": "A", ...further fields},
{ "name": "B", ...further fields},
...
]

Apparently, $set cannot be used for this case since I receive the following error: Error while bulk writing *v1.UserCollection (FailedToParse) Modifiers operate on fields but we found type array instead.

Any help is highly appreciated!

Upvotes: 1

Views: 962

Answers (1)

icza
icza

Reputation: 417462

You may use Collection.BulkWrite().

Since you want to update each document differently, you have to prepare a different mongo.WriteModel for each document update.

You may use mongo.ReplaceOneModel for individual document replaces. You may construct them like this:

wm := make([]mongo.WriteModel, len(users)
for i, user := range users {
    wm[i] = mongo.NewReplaceOneModel().
        SetUpsert(true).
        SetFilter(bson.M{"name": user.name}).
        SetReplacement(user)
}

And you may execute all the replaces with one call like this:

res, err := coll.BulkWrite(ctx, wm)

Yes, here we too have a loop, but that is to prepare the write tasks we want to carry out. All of them is sent to the database with a single call, and the database is "free" to carry them out in parallel if possible. This is likely to be significantly faster that calling Collection.ReplaceOne() for each document individually.

Upvotes: 3

Related Questions