0

I want to make an update query for multiple subdocuments at once. I will be setting all the subdocuments statuses to passive where they satisfy the condition I give on query.

db.deduplications.update(  
{ $and: [ 
    { "_id": "189546D623FC69E3B693FDB679DBC76C" }, 
    { "DeviceVersionPairs.DeviceId": ObjectId("5822d0606bfdcd6ec407d9b9") }, 
    { "DeviceVersionPairs.CloudFolderId": ObjectId("5823110e6bfdd46ec4357582") },
    { "DeviceVersionPairs.CloudFileId": ObjectId("582311168cd396223499942a") },
    { "DeviceVersionPairs.VersionId": ObjectId("582311168cd396223499942b") }
] },
{ $set: { "DeviceVersionPairs.$.Status": "passive" }});

Above query finds exactly one subdocument, then it makes the update as I want it. But;

db.deduplications.update(  
{ $or: [ { $and: [ 
        { "_id": "189546D623FC69E3B693FDB679DBC76C" }, 
        { "DeviceVersionPairs.DeviceId": ObjectId("5822d0606bfdcd6ec407d9b9") }, 
        { "DeviceVersionPairs.CloudFolderId": ObjectId("5823110e6bfdd46ec4357582") },
        { "DeviceVersionPairs.CloudFileId": ObjectId("582311168cd396223499942a") },
        { "DeviceVersionPairs.VersionId": ObjectId("582311168cd396223499942b") }
    ] } ,   
    { $and: [ 
        { "_id": "189546D623FC69E3B693FDB679DBC76C" }, 
        { "DeviceVersionPairs.DeviceId": ObjectId("56dfe1356caaea14a819f1e4") }, 
        { "DeviceVersionPairs.CloudFolderId": ObjectId("583fb4bc6e7f341874f13bfc") }, 
        { "DeviceVersionPairs.CloudFileId": ObjectId("583fb539e015b8a53fb71872") }, 
        { "DeviceVersionPairs.VersionId": ObjectId("583fb4ca6e7f331874213584") }
    ] } ] },
{ $set: { "DeviceVersionPairs.$.Status": "passive" }});

When I populate the query segment with an or, and add the other items it gives error:

WriteResult({
    "nMatched" : 0,
    "nUpserted" : 0,
    "nModified" : 0,
    "writeError" : {
        "code" : 16837,
        "errmsg" : "The positional operator did not find the match needed from the query. Unexpanded update: DeviceVersionPairs.$.Status"
    }
})

What am I missing here?

Tolga Evcimen
  • 7,112
  • 11
  • 58
  • 91
  • do you mean that you just wanna update the sub docs in _id = "189546D623FC69E3B693FDB679DBC76C", and the DeviceId is two of your query, but don't update other sub docs? – John Zeng Dec 01 '16 at 06:48
  • I guess yes. Other `_id` may present in the `or` level, and not all the subdocuments are needed to be updated of all given `_id`s. – Tolga Evcimen Dec 01 '16 at 06:54
  • What is the reason of the downvote? Actually one might come here and learn that `$` positional operator cannot match with multiple subdocuments. Isn't this beneficial for the community? – Tolga Evcimen Dec 01 '16 at 08:38

5 Answers5

3

You got this error

The positional operator did not find the match needed from the query

because of your second query matched with multiple sub-document. Currently it is not possible to use the positional operator to update all items in an array using positional operator.

so to solve your problem you can follow this process

  1. Find document using _id and to find sub document using $elemMatch and then

  2. update each sub document and save document again

can try like this:

db.deduplications.find(  
{ $or: [ {
         "_id": ObjectId("583fc558668bde730a460e11") , 
        "DeviceVersionPairs":{
            $elemMatch:{ "DeviceId": ObjectId("5822d0606bfdcd6ec407d9b9") , 
            "CloudFolderId": ObjectId("5823110e6bfdd46ec4357582") ,
            "DeviceVersionPairs.CloudFileId": ObjectId("582311168cd396223499942a") ,
            "DeviceVersionPairs.VersionId": ObjectId("582311168cd396223499942b") }}
    } ,   
    { 
        "_id": ObjectId("583fc558668bde730a460e11") , 
        "DeviceVersionPairs":{
            $elemMatch:{ "DeviceId": ObjectId("56dfe1356caaea14a819f1e4") , 
            "CloudFolderId": ObjectId("583fb4bc6e7f341874f13bfc") , 
            "CloudFileId": ObjectId("583fb539e015b8a53fb71872") , 
            "VersionId": ObjectId("583fb4ca6e7f331874213584") }}
    } ] 
}).forEach(function (doc) {
    doc.DeviceVersionPairs.forEach(function (device) {
      device.status = 'passive';
    });
    db.deduplications.save(doc);
 });
Shaishab Roy
  • 16,335
  • 7
  • 50
  • 68
  • Actually this is what I was trying to avoid. My query is already in this shape, now I am trying to increase the performance by making all update at once. But I am feeling now that it is not possible. Right? – Tolga Evcimen Dec 01 '16 at 08:02
  • This is enough of an answer to me right now. Thank you. – Tolga Evcimen Dec 01 '16 at 08:05
  • There is now a feature to allow this sort of update. https://jira.mongodb.org/browse/SERVER-1243 – Brightstar Feb 06 '18 at 23:56
0

Not so sure about what do you want, but if I were right, what you want may be $elemMatch.

Refer to MongoDB Update Manual , and $elemMatch

When you wanna query into embedded documents, you can't use $and and $or directly, this is some kind of 'bug' of mongodb.

John Zeng
  • 1,174
  • 2
  • 9
  • 22
0

Use method UpdateMany() to update documents. According to framework and langauage this method will be different like Update({multi : true}); or UpdateAll({});`

For example :

db.collection.updateMany(
   <filter>,
   <update>
);

Use $set to write.

Ref: https://docs.mongodb.com/v3.2/reference/method/db.collection.updateMany/

try {
   db.restaurant.updateMany(
      { $or: []
      },
      { $set: { "DeviceVersionPairs.$.Status": "passive" } }
   );
} catch (e) {
   print(e);
} 
yojna
  • 503
  • 1
  • 5
  • 17
  • Might be the way you are trying to filter records is not correct. You can't update multiple records by finding records using `_id`. – yojna Dec 01 '16 at 08:16
  • actually not related to the `_id` in the query, but the `$` positional operator. It is not able to match with multiple subdocuments. That's what I learned today. – Tolga Evcimen Dec 01 '16 at 08:36
0

I have run below query. And it is working fantastic.

db.mm.update( { $or: [ {$and: [{"_id": ObjectId("581d18d41b6c5c752f11c87a")}] }, 
                       {$and: [{"_id": ObjectId("581a1a671b6c5c752f11c87b")}] } 
               ] 
          }, {$set : {"name": "abc3"}});

If first document not found , second one is updated. And if 1st one is found it is updating only 1st one.

You can see that in your question -> Error message it is showing "nMatched" : 0 . It is not finding any documents. Confirm all ID's and reference ID's.

yojna
  • 503
  • 1
  • 5
  • 17
  • I can confirm that Id are correct. I was, just like you, suspicious about that response, hence I made an `.find` query too with the same `{$or: []}` query, and it returns the documents no problem. The problem is as @shaishab roy remarks with the positional operator `$`. It is not able to match with multiple subdocuments. – Tolga Evcimen Dec 01 '16 at 09:00
0

please see whether array filters will help

db.deduplications.updateMany(
{ $or: [ { $and: [ 
        { "_id": "189546D623FC69E3B693FDB679DBC76C" }, 
        { "DeviceVersionPairs.DeviceId": ObjectId("5822d0606bfdcd6ec407d9b9") }, 
        { "DeviceVersionPairs.CloudFolderId": ObjectId("5823110e6bfdd46ec4357582") },
        { "DeviceVersionPairs.CloudFileId": ObjectId("582311168cd396223499942a") },
        { "DeviceVersionPairs.VersionId": ObjectId("582311168cd396223499942b") }
    ] } ,   
    { $and: [ 
        { "_id": "189546D623FC69E3B693FDB679DBC76C" }, 
        { "DeviceVersionPairs.DeviceId": ObjectId("56dfe1356caaea14a819f1e4") }, 
        { "DeviceVersionPairs.CloudFolderId": ObjectId("583fb4bc6e7f341874f13bfc") }, 
        { "DeviceVersionPairs.CloudFileId": ObjectId("583fb539e015b8a53fb71872") }, 
        { "DeviceVersionPairs.VersionId": ObjectId("583fb4ca6e7f331874213584") }
    ] } ] },


     { $set: {
         "DeviceVersionPairs.index.Status": "passive" ,
       }
     },
    { 
    arrayFilters: [ 
            { $or: [ { $and: [ 
        { "index.DeviceId": ObjectId("5822d0606bfdcd6ec407d9b9") }, 
        { "index.CloudFolderId": ObjectId("5823110e6bfdd46ec4357582") },
        { "index.CloudFileId": ObjectId("582311168cd396223499942a") },
        { "index.VersionId": ObjectId("582311168cd396223499942b") }
    ] } ,   
    { $and: [ 
        { "index.DeviceId": ObjectId("56dfe1356caaea14a819f1e4") }, 
        { "index.CloudFolderId": ObjectId("583fb4bc6e7f341874f13bfc") }, 
        { "index.CloudFileId": ObjectId("583fb539e015b8a53fb71872") }, 
        { "index.VersionId": ObjectId("583fb4ca6e7f331874213584") }
    ] } ] }
          ]
    ,"multi":true, new:true
    },

    function (err, out) {
         //
    });
Raghu Vallikkat
  • 365
  • 5
  • 16