1

I was trying to push to a nested array in MongoDB 3.0.4. This will illustrate the problem- here is a document in the characters collection and I want to add image_4 to Elmer's images array:

{
    "_id" : ObjectId("56084e91981824fc51693e72"),
    "firstname" : "Elmer",
    "lastname" : "Fudd",
    "company" : "Warners",
    "password" : "4567",
    "galleries" : [
        {
            "gallery" : "1",
            "images" : [
                "image_1",
                "image_2",
                "image_3"
            ]
        }
    ]
}

Firstly I tried:

db.characters.update({"firstname":"Elmer"},{$push {"galleries.$.images":"image_4"}})

and got the error:

"writeError" : {
        "code" : 16837,
        "errmsg" : "The positional operator did not find the match needed from the query. Unexpanded update: galleries.$.images"

Then I saw a solution on SO Update an item in an array that is in an array and tried:

db.characters.update({"firstname":"Elmer"},{$push:{"galleries.0.images":"image_4"}})

which worked fine. I understand that the positional operator $ cannot be used with nested arrays, but why does its substitution with 0 work, and what is 0 in this usage? I can't find it in the Mongodb docs.

Community
  • 1
  • 1
meanstacky
  • 387
  • 1
  • 8
  • 18
  • The error is very descriptive, you are not matching an element of the array. So instead do: `{"firstname":"Elmer", "galleries.gallery":1}` in the query portion of your update so you actually match the required element. – Blakes Seven Sep 28 '15 at 01:06
  • @Blakes Seven. db.characters.update({"firstname":"Elmer", "galleries.gallery":1},{$push:{"images":"image_4"}}) isn't working. No error message but nModified:0. – meanstacky Sep 28 '15 at 01:41
  • Because that time you are not using the positional `$` operator at all. In order to use the positional operator you need to match an element in the array and then specifiy the operator within the update portion. Just alter the first query you wrote to also include a match for the array element. – Blakes Seven Sep 28 '15 at 01:43
  • db.characters.update({"firstname":"Elmer", "galleries.gallery":1},{$push:{"galleries.$.images":"image_6"}}) also isn't working. No error message but nModified:0. – meanstacky Sep 28 '15 at 03:07
  • 1
    "galleries.gallery" is a "string" and not a numeric value. So use `"1"` instead. This is all plainly stated in the [documentation](http://docs.mongodb.org/manual/reference/operator/update/positional/) or it is just plain typing mistakes or mixed types. As was the original error message quite clear as to what was missing. – Blakes Seven Sep 28 '15 at 03:24

1 Answers1

1

In this usage, 0 would translate to

The first element of the 0 based array stored in the galleries field of the first document in which the field firstname equals "Elmar".

Which of course worked in this instance. However, arrays are not guaranteed to be returned in the same order for each query. So if you had two galleries, gallery 1 could be returned as the second array element.

The problem here is that your query didn't really reflect what you wanted to do. What you really wanted to do is

In the document in which the field firstname equals "Elmar", add "img_4" to the array element in galleries in which the field gallery equals 1.

So, how would we achieve that? Basically, you were on the right way by using the $ operator. However, your query did not include a matching pattern for the array, which is mandatory (how else could the query engine identify the exact array element for update). So your query needs to be modified a bit:

db.characters.update(
  // The query part
  {
   // Nothing new here
   "firstname": "Elmar",
   // Now we want to identify the gallery:
   // "Of all elements in the galleries array..."
   "galleries": {
      // "...find the element..."
      "$elemMatch":{
        // "...in which the field gallery equals 1."
        "gallery": "1"
      }
    }
  },
  // Update part
  { 
    // You want to use $addToSet instead of $push here
    // since you don't want to have an image twice in a gallery
    "$addToSet":{
      // $ operator works now since we have identified the gallery
      // in the query part
      "galleries.$.images":"image_4"
    }
  }
)

Please have a look at the docs for the positional $ parameter for details.

Side note: There is a 16MB document size limit for BSON documents as of the time of this writing, so you probably should rethink your model. However, this is a different story to tell (and how to model many-to-many relations properly in MongoDB has been asked a felt million times before).

Markus W Mahlberg
  • 19,711
  • 6
  • 65
  • 89