Search code examples
mongodbfilterdynamicrealm

MongoDB updateMany with dynamic filter


How can I use UpdateMany with dynamic filters in the MongoDB Realm Node.js SDK?

I have an array of objects and I want to update/insert each object in the database using UpdateMany with dynamic filters. Here's an example of the data:

const docs = [
   { id: '1', name: 'John Doe', age: 30 },
   { id: '2', name: 'Jane Doe 2', age: 25 },
   { id: '3', name: 'Bob Smith', age: 35 },
   { id: '4', name: 'Bob gm 2', age: 20 },
   { id: '5', name: 'bbbbb', age: 21 },
]

I want to use the id field of each object in the docs array as the filter to update or insert the object. The MongoDB Realm Node.js SDK doesn't support bulk operations, so I'm looking for an alternative way to achieve this.

Here's the code I have so far:

const filter = { id: { $in: docs.map(doc => doc.id) } }
const update = { $set: {} }
const options = { upsert: true } 
const result = await collection.updateMany(filter, update, options)

if I pass docs to { $set: docs }, mongodb returns error

Modifiers operate on fields but we found type array instead

I have larger dataset so can't use updateone in loop, that is not the option.


Solution

  • You can't achieve this using updateMany, but if you want to do it in a single operation. You can create a dummy collection and insert your documents into that collection. After that, use an aggregation pipeline to merge your temporary collection, with the original collection. Like this:

    const docs = [
       { id: '1', name: 'John Doe', age: 30 },
       { id: '2', name: 'Jane Doe 2', age: 25 },
       { id: '3', name: 'Bob Smith', age: 35 },
       { id: '4', name: 'Bob gm 2', age: 20 },
       { id: '5', name: 'bbbbb', age: 21 },
    ];
    await tempCollection.insertMany(docs);
    await tempCollection.aggregate(
      [
        {
          $merge: {
              into: "collection",
              on: "_id",
              whenMatched: "replace",
              whenNotMatched: "insert",
          }
        }
      ]
    );
    await tempCollection.remove({});
    

    You can wrap all three queries in a transaction, it's a good thing to do. Also, I am additionally cleaning the tempCollection after merging, it's an optional step, use it if you find it useful.