4

What's the correct syntax for doing a NodeJS/MongoDB updateOne with arrayFilters in a bulk operation?

MongoDB version: 4.2.5
NodeJS version: 12.x
npm mongodb version: 3.6.0

Given the following simplified collection. I need to update an element in the lineItems array.

{
  _id: ObjectId("5d50689fd304e3189aae99ba"),
  "lineItems" : [
    { "importId" : "abc123" },
    { "importId" : "def456" }
  ]
}

The following bulk update works in the MongoDB shell:

var bulk = db.myCollection.initializeOrderedBulkOp();
bulk
  .find({"_id": ObjectId(myId)})
  .arrayFilters( [ { "elem.importId": "abc123" } ] )
  .updateOne( { $set: { "lineItems.$[elem].meta": { "test": 1 } } } );
bulk.execute();

The following works in NodeJS (but it's NOT a bulk operation):

db.collection('myCollection').updateOne(
  { _id: ObjectID(myId) },
  { $set: { 'lineItems.$[elem].meta': { test: 1 } } },
  { arrayFilters: [{ 'elem.importId': lineItem.importId }] }
);

This ticket implies that the NodeJS MongoDB driver supports arrayFilters on bulk operations:
https://jira.mongodb.org/browse/NODE-1911

However, I've tried many different variations. But for example, this code returns error "arrayFilters is not a function".

const bulkOp = db.collection('myCollection').initializeOrderedBulkOp()
bulkOp
  .find({ _id: ObjectId(myId) })
  .arrayFilters([{ 'elem.importId': lineItem.importId }])
  .updateOne({ $set: { 'lineItems.$[elem].meta': { test: 1 } } });

The above is based on the example in the MongoDB documentation:

bulk
  .find({}).
  arrayFilters( [ { "elem.grade": { $gt: 85 } } ] ).
  updateOne( { $set: { "grades.$[elem].mean" : 70 } } );

https://docs.mongodb.com/v4.2/reference/method/Bulk.find.arrayFilters/#Bulk.find.arrayFilters

4
  • Are you executing in shell? if yes then what is the version of shell? if no then can you try to execute in shell. Commented Aug 4, 2020 at 18:11
  • 1
    I think you're referring to the MongoDB shell. Good question. Yes - I've added to the question a working bulk update query that works when run in the MongoDB shell. Commented Aug 4, 2020 at 18:34
  • one more, You are using this npm and this is the documentation, and this npm uses mongodb driver api, am i right? if yes then can you check they have explained arrayFilters function in documentation? because i did't find. Commented Aug 4, 2020 at 19:07
  • Your links to both npm and the documentation are correct. I couldn't find any mention of arrayFilters in the documentation either. However, arrayFilters does appear in the npm package source code. The jira ticket I linked to in the question "Ability to use array filters with bulk operations" has resolution = fixed and hints that it's supported (but maybe it's not and that's the problem here). Commented Aug 4, 2020 at 19:23

1 Answer 1

2

It looks like arrayFilters is not yet supported properly by the MongoDB node.js driver (as of (3.6.0).

There is a jira ticket with a workaround of using "raw": https://jira.mongodb.org/browse/NODE-2101

There's a good vanilla node.js driver example in the ticket. However, if you're trying to use Mongoose here's another solution based on the MongoDB documentation:

MongoDB document from "students3" collection:

{
   "_id" : 1,
   "grades" : [
      { "type" : "quiz", "questions" : [ 12, 10, 5 ] },
      { "type" : "quiz", "questions" : [ 10, 11, 6 ] },
      { "type" : "hw", "questions" : [ 5, 4, 3 ] },
      { "type" : "exam", "questions" : [ 25, 10, 23, 0 ] }
   ]
}

Mongoose Code (MongoDb 4.2.8 using Mongoose 5.9.28):

// @ts-ignore (required if "raw" is not part of @types)
const bulk = Students.collection.initializeOrderedBulkOp();
bulk.raw({
    updateOne: {
        updateOne: {
            filter: {_id: mongoose.Types.ObjectId("5ed3243aa83d3a42ccff43e2")},
            update: { $inc: { "grades.$[].questions.$[score]": 2 } },
            arrayFilters: [  { "score": { $gte: 8 } } ]
        }
    }
});

Also I found that you need to use $eq within arrayFilters if you're trying to filter based on an ObjectId.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.