0

Im tring to set 0 the items.qty of a document obtains by a id query.

db.warehouses.update(
    // query 
    {
        _id:ObjectId('5322f07e139cdd7e31178b78')
    },    
    // update 
    {
        $set:{"items.$.qty":0}
    },    
    // options 
    {
        "multi" : true,  // update only one document 
        "upsert" : true  // insert a new document, if no existing document match the query 
    }
);

Return:

Cannot apply the positional operator without a corresponding query field containing an array.

This is the document that i want to set all items.qty to 0

{
  "_id": { "$oid" : "5322f07e139cdd7e31178b78" },
  "items": [
    {
      "_id": { "$oid" : "531ed4cae604d3d30df8e2ca" },
      "brand": "BJFE",
      "color": "GDRNCCD",
      "hand": 1,
      "model": 0,
      "price": 500,
      "qty": 0,
      "type": 0
    },
    {
      "brand": "BJFE",
      "color": "GDRNCCD",
      "hand": 1,
      "id": "23",
      "model": 0,
      "price": 500,
      "qty": 4,
      "type": 0
    },
    {
      "brand": "BJFE",
      "color": "GDRNCCD",
      "hand": 1,
      "id": "3344",
      "model": 0,
      "price": 500,
      "qty": 6,
      "type": 0
    }
  ],
  "name": "a"
}

2 Answers 2

4

EDIT

The detail missing from the question was that the required field to update was actually in a sub-document. This changes the answer considerably:


This is a constraint of what you can possibly do with updating array elements. And this is clearly explained in the documentation. Mostly in this paragraph:

The positional $ operator acts as a placeholder for the first element that matches the query document

So here is the thing. Trying to update all of the array elements in a single statement like this will not work. In order to do this you must to the following.

db.warehouses.find({ "items.qty": { "$gt": 0 } }).forEach(function(doc) {
    doc.items.forEach(function(item) {
        item.qty = 0;
    });
    db.warehouses.update({ "_id": doc._id }, doc );
})

Which is basically the way to update every array element.

The multi setting in .update() means across multiple "documents". It cannot be applied to multiple elements of an array. So presently the best option is to replace the whole thing. Or in this case we may just as well replace the whole document since we need to do that anyway.

For real bulk data, use db.eval(). But please read the documentation first:

db.eval(function() {
    db.warehouses.find({ "items.qty": { "$gt": 0 } }).forEach(function(doc) {
        doc.items.forEach(function(item) {
            item.qty = 0;
        });
        db.warehouses.update({ "_id": doc._id }, doc );
    });
})

Updating all the elements in an array across the whole collection is not simple.


Original


Pretty much exactly what the error says. In order to use a positional operator you need to match something first. As in:

db.warehouses.update(
    // query 
    {
        _id:ObjectId('5322f07e139cdd7e31178b78'),
        "items.qty": { "$gt": 0 }
    },    
    // update 
    {
        $set:{"items.$.qty":0}
    },    
    // options 
    {
        "multi" : true,  
        "upsert" : true  
    }
);

So where the match condition fins the position of the items that are less than 0 then that index is passed to the positional operator.

P.S : When muti is true it means it updates every document. Leave it false if you only mean one. Which is the default.

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

7 Comments

Not works OK, this inly update the first item with qty>0..I need to update all, i set multi to true but dotn work.
@colymore I think you should remove the upsert: true for starters since you are not intending to insert new records. The multi works as intended. The comment I gave was because you have the ObjectId in your match criteria. That implies one item. Remove that and keep multi as true when you want to update everything.
I edit the question with a document example, that i want is set all qty in document.items array to 0..But only update the first item with qty 0..
@colymore Give me 5 minutes then refresh. This is not the only time I have seen you post a question in this manner. You need to understand what sub-documents are. Until now your question has been misleading.
all the documents that i found are in english, and its very difficult for me read and understant them, and i try with code by try-error..I will read more of subdocuments in mongoDB.Thanks
|
2

You can use the $ positional operator only when you specify an array in the first argument (i.e., the query part used to identify the document you want to update).

The positional $ operator identifies an element in an array field to update without explicitly specifying the position of the element in the array.

3 Comments

I don't want to discourage you because you have been giving good answers. As help, try to use the > to mark "block quotes" when quoting from documentation. A good guide is here. Keep up the work. You clearly understand enough to help others considerably.
Thank you, Neil. I hadn't read the guide. I will review it completely before I post again.
No problem. Look up "tagging" in the page as well. I only saw this because I was on the page already.

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.