Answer a question

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

Answers

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.

Logo

MongoDB社区为您提供最前沿的新闻资讯和知识内容

更多推荐