12

Is there a way in mongodb to use if/else to set a field value during an update. i know that i can use find, to return documents, loop over them, and do if/else check on each and make a new save query for each of the documents.

However, that seems wasteful, if there is a way to update conditionally in one go.

Is it possible to conditionally set a field value, e.g like this

Documents.update(
    {some_condition: true}, 
    {$set: {"status": 
        {$cond: 
              {if  : {"some field": "some condition"}},
              {then:  "value 1"} ,
              {else: "value 2"} 
        } 
    }} 
)

(I know that $cond is used for aggregation, i used it here as an example of what i have in mind.)

Rainer Plumer
  • 3,693
  • 2
  • 24
  • 42
  • why wouldnt you put that logic in your application code? If your determine to run database side you can use a saved function (although not recommended) http://docs.mongodb.org/manual/tutorial/store-javascript-function-on-server/ – Rob May 21 '15 at 23:07
  • 2
    @Rob I checked out the saved function approach ,and it seems to have too many negative sides for me to use it. The logic would basically remain in the application, its just one conditional statement. I want to avoid looping over the returned documents, and saving each of those separately. If there is a way to update conditionally, then i could update lots of documents in one query. – Rainer Plumer May 21 '15 at 23:29

2 Answers2

7

MongoDB doesn't support the sort of conditional update you're looking for. However, you can still do better than using a find, loop, and save approach.

Move the condition check into the update query selector and then issue two updates (one for each case), using {multi: true} to apply the update to all matched docs.

// Start with the "if" update
Documents.update(
    {some_condition: true, "some field": "some condition"}, 
    {$set: {"status": "value 1"}},
    {multi: true},
    function(err, numAffected) {
        // Now do the "else" update, using $ne to select the rest of the docs
        Documents.update(
            {some_condition: true, "some field": {$ne: "some condition"}}, 
            {$set: {"status": "value 2"}},
            {multi: true},
            function(err, numAffected) {
                // All done.
            }
        )
    }
)
JohnnyHK
  • 305,182
  • 66
  • 621
  • 471
  • 1
    I try to emulate this approach by ...`{some_condition: true, "some field": " condition 1"}`, ...`{some_condition: true, "some field": "condition 2"}` (everything else stays the same except I did not include {multi: true}). For some reason only the first update (under condition 1) took place. If I include {multi: true} for both updates, I get 'Fourth argument must be empty when specifying upsert and multi with an object'. – Treefish Zhang May 26 '17 at 04:00
  • @TreefishZhang Go ahead and post that as a new question if you're still having trouble. – JohnnyHK May 26 '17 at 04:35
  • [Posted](https://stackoverflow.com/questions/44136340/how-to-update-mongodb-dynamic-attributes/44204847#44204847) ! – Treefish Zhang May 26 '17 at 15:08
  • @TreefishZhang You appear to have posted that question as an answer to another question instead of its own question. – JohnnyHK May 26 '17 at 15:29
  • @Neil Lunn pointed out that I was running the code in in synchronous Mongo shell and that callbacks do not work in it. I ran the code with Node and both are updated correctly. – Treefish Zhang May 27 '17 at 03:40
  • @JohnnyHK is this answer still correct? I see this functionality https://docs.mongodb.com/manual/reference/operator/aggregation/cond/ – Alexander Mills Aug 23 '17 at 19:59
  • basically, I want to update a field only if the field does not exist / is null, but I always want to update other fields using the same query. – Alexander Mills Aug 23 '17 at 19:59
  • @JohnnyHK, would be much obliged if you had some advice for me here: https://stackoverflow.com/questions/46715946/conditional-set-in-mongo-findoneandupdate – user1272965 Oct 12 '17 at 19:19
  • @AlexanderMills : I know this is late but might be helpful for someone, we can't do update(or writes to db) operation using aggregation, basically aggregation can be helpful to fine tune your final result(used for fast results on find/read operation) – whoami - fakeFaceTrueSoul Jun 27 '19 at 16:50
5

Answer: Yes

It's pretty much possible using the $set stage in the aggregation pipeline

  1. Use $set to compute the conditional value in each record
  2. Use $out to update the records in the collection

Example:

db.collectionname.aggregate(
    [
        {
            $set: {                   //Compute the new value
                'ResultField': {
                    $cond: {
                        if: {
                            $lt: ['$ScoreField', 40]
                        },
                        then: "Fail",
                        else: "Pass"
                    }
                }
            }
        },
        {
            $out: 'collectionname'    //Update the records in collection
        }
    ]
)
Alpesh Patil
  • 1,672
  • 12
  • 15