user1175817
user1175817

Reputation: 489

Create document on update only if condition is met mongodb

Please see mongo query below. I want to be able to update a document if it meets the conditions. I want to create a new document if x or y arent met but i dont want to create a new document if x and y are met but z has a value

 collection.update({ x : 1, y : 2, z : {$exists : false} }, {field_to_update : update}, { upsert :true })

Upvotes: 2

Views: 2943

Answers (2)

user1175817
user1175817

Reputation: 489

The solution was to create a unique compound index on the first two fields. Thanks

Upvotes: 2

Neil Lunn
Neil Lunn

Reputation: 151170

There is a "possible" way to do this, but you have to be very careful about what you are even considering here. Consider the following initial documents:

{ "x" : 1, "y" : 2, "z" : 4, "a" : 2 }
{ "x" : 1, "y" : 2, "a" : 2 }
{ "x" : 1, "y" : 2, "z" : 5, "a" : 4 }
{ "x" : 4, "y" : 2, "a": 1 }

Your basic conditions say that you want to match on "x" and "y" but only insert a new document if there is not a match on "z" for any value. Presumably though if "x" and "y" do not match then you want to insert a new document.

The main consideration here is "which" document do you want to update? There may be as shown above multiple matches to this condition, which would ideally match two of the documents here and not the document that has { "x": 1, "y": 2 } but no value for "z". If you are okay with that as a logic principle then the following might be of use:

db.test.update(
    { 
        "x": 1, 
        "y": 2, 
        "z": { "$exists": true },
    }, 
    { 
        "$set": { "a": 3 }, 
        "$setOnInsert": { "z": 3 }
    },
    { 
        "multi": true, 
        "upsert": true
    }
)

The important part here is the use of the $exists operator since you don't want to modify anything that simply didn't have anything for "z" at all, you could additionally constrain that to numeric types if needed but the main point is missing the document you do not want to update.

Since you are not specifying a value of "z" to match on the query side, it is likely that you do have something you want when you "upsert", so include that in the $setOnInsert modifier as we show what happens when the conditions do not match:

db.test.update(
    { 
        "x": 4, 
        "y": 2, 
        "z": { "$exists": true },
    }, 
    { 
        "$set": { "a": 3 }, 
        "$setOnInsert": { "z": 3 }
    },
    { 
        "multi": true, 
        "upsert": true
    }
)

So while there was a document there with the same values for "x" and "y" as given in the statement, it does not have any value for "z" and so a new document would be inserted by the "upsert" condition. The resulting documents from the two statements now look like this:

{ "x" : 1, "y" : 2, "z" : 4, "a" : 3 }
{ "x" : 1, "y" : 2, "a" : 2 }
{ "x" : 1, "y" : 2, "z" : 5, "a" : 3 }
{ "x" : 4, "y" : 2, "a" : 1 }
{ "x" : 4, "y" : 2, "a" : 3, "z" : 3 }

As long as you can live with the consideration that "any value" for "z" can easily result in multiple documents being updated, then this is the way to handle the "upsert" conditions that you want.

Upvotes: 1

Related Questions