3

I'm having a bit of a mongo issue. I was wondering if there was a way to do the following in a mongo console command rather then multiple find and update calls.

{
    "_id" : ObjectId("50b429ba0e27b508d854483e"),
    "array" : [
        {
            "id" : "1",
            "letter" : "a"
        },
        {
            "id" : "2",
            "letter" : "b"
        }
    ],
    "tester" : "tom"
}

I want to update the object with this new array item

{
    "id": "2",
    "letter": "c"
}

I used this, addToSet is limited, it won't insert an item into the array if it is already there but it will not update an item based on an identifier. In this case I would really like to update this entry based on the id.

db.soup.update({
     "tester": "tom"
 }, {
     $addToSet: {
         "array": {
             "id": "2",
             "letter": "c"
         }
     }
});

This gives me:

{
    "_id" : ObjectId("50b429ba0e27b508d854483e"),
    "array" : [
        {
            "id" : "1",
            "letter" : "a"
        },
        {
            "id" : "2",
            "letter" : "b"
        },
        {
            "id" : "2",
            "letter" : "c"
        }
    ],
    "tester" : "tom"
}

When what I really wanted was:

{
    "_id" : ObjectId("50b429ba0e27b508d854483e"),
    "array" : [
        {
            "id" : "1",
            "letter" : "a"
        },
        {
            "id" : "2",
            "letter" : "c"
        }
    ],
    "tester" : "tom"
}

2 Answers 2

7

You can use the $ positional operator to do this:

db.soup.update(
    {_id: ObjectId("50b429ba0e27b508d854483e"), 'array.id': '2'}, 
    {$set: {'array.$.letter': 'c'}})

The $ in the update object acts as a placeholder for the first element of array to match the query selector.

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

3 Comments

This is great. I just realized that this is what I have. The more complicated question is now, how do I also get it to push a new object eg {"id":3,"letter":"d"} in a sort of upsert kind of way into the array? Do I have to programmatically sort the requests out? If it exists use $set if not $push?
@ThomasReggi It would be better if you asked that as a separate question.
0

Here you go:

> db.collection.insert( { array : [ { id : 1, letter : 'a' }, { id : 2, letter : 'b' } ], tester : 'tom' } );
> db.collection.findOne();
{
    "_id" : ObjectId("50b431a69a0358d590a2f5f0"),
    "array" : [
        {
            "id" : 1,
            "letter" : "a"
        },
        {
            "id" : 2,
            "letter" : "b"
        }
    ],
    "tester" : "tom"
}
> db.collection.update( { tester : 'tom' }, { $set : { 'array.1' : { id : 2, letter : 'c' } } }, false, true );
> db.collection.findOne();
{
    "_id" : ObjectId("50b431a69a0358d590a2f5f0"),
    "array" : [
        {
            "id" : 1,
            "letter" : "a"
        },
        {
            "id" : 2,
            "letter" : "c"
        }
    ],
    "tester" : "tom"
}

The trick lies in the false, true, false. That is: true for upsert, false for update multiple.

For more details check out: http://www.mongodb.org/display/DOCS/Updating#Updating-update%28%29

2 Comments

You are updating based on the array position (array.1) rather than finding the entry where id is 2. This only works if you already know the position of the element in the array, rather than just the id.
The question implied that the criterion was { tester : 'tom' } and that he wanted to replace the second element of the 'array' list. If the criterion must also include { 'array.id' : 2 } then JohnnyHK's solution looks like the winner here.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.