1

i am pushing sub documents into an array. Basically the subdocs are something like this:

{
  id:"someId",
  date:Date
}

i want to add new subdoc only if the theres no other subdoc with matching id.I found $addToSet modifier on mongodb documentation but i am not sure if it can just check a key inside a sub document rather than comparin entire array item to another one. Is there any way to do this in single query or anyway should i go with 2 queries?

nikoss
  • 3,254
  • 2
  • 26
  • 40

1 Answers1

3

You can do this in a single atomic update operation. Suppose your collection has the following documents:

Mongo shell

db.test.insert([
    { 
        "_id": 1,
        "items": [
            { "id": 1, "date": new Date("2016-05-20") },
            { "id": 2, "date": new Date("2016-05-21") }
        ]
    },
    { 
        "_id": 2,
        "items": [
            { "date": new Date("2016-05-22") }
        ]
    }
])

Then the following update statement in mongo shell will update the document that has the items subdoc which does not have the id key by adding the new sub document { "id": 3, "date": new Date() } to the items array:

db.test.update(
    { "items.id": { "$exists": false } },
    {
        "$push": {
            "items": { "id": 3, "date": new Date() }
        }
    }
)

Querying the test collection

> db.test.find().pretty()
{
        "_id" : 1,
        "items" : [
                {
                        "id" : 1,
                        "date" : ISODate("2016-05-20T00:00:00Z")
                },
                {
                        "id" : 2,
                        "date" : ISODate("2016-05-21T00:00:00Z")
                }
        ]
}
{
        "_id" : 2,
        "items" : [
                {
                        "date" : ISODate("2016-05-22T00:00:00Z")
                },
                {
                        "id" : 3,
                        "date" : ISODate("2016-05-23T09:33:15.913Z")
                }
        ]
}    

To add a new subdoc only if the there's no other subdoc with a matching id, you only need one update operation where you can specify the matching condition in the update query document.

The following update operation will add a subdocument with id = 4 to the array since there is no matching array element in the existing document:

db.test.update(
    { "items.id": { "$ne": 4 } },
    {
        "$push": {
            "items": { "id": 4, "date": new Date() }
        }
    }
)

For an exclusive update where you just want to add the subdocument if there is an id field AND the given id does not match, then include the condition together with the equality check:

db.test.update(
    { 
        "items.id": { 
            "$exists": true,
            "$ne": 4 
        } 
    },
    {
        "$push": {
            "items": { "id": 4, "date": new Date() }
        }
    }
)
chridam
  • 100,957
  • 23
  • 236
  • 235
  • i am talking about pushing into the array only if theres no matching id – nikoss May 23 '16 at 09:50
  • 1
    @nikoss This is considered a bad practice here on SO the way you changed the question; _i want to add new subdoc only if id doesn't exist._ is completely different to _i want to add new subdoc only if the theres no other subdoc with matching id_. The above answer directly addresses the original question _i want to add new subdoc only if id doesn't exist._ – chridam May 23 '16 at 10:06