3
{
 _id:'1',
name:'apple',
option:[{
weight:'10',
size:'40'
price:'40',
},
{weight:'40',
 size:'40'
price:'200'}]
}

If weight or the size is different I want to upsert new object in the array, if both the size and the weight matched I want to update it. How can I do it?

2
  • Surely you mean that "ALL" array items are meant to have both "weight" and "size". In your quick copy/paste you have not shown that. Commented Oct 18, 2017 at 9:15
  • yes, my mistake, I edited it Commented Oct 18, 2017 at 9:19

2 Answers 2

3

Similar to your previous question, you use .bulkWrite() but since the array element selection has "multiple conditions" this is where you use $elemMatch:

db.collection.bulkWrite([
  { "updateOne": {
    "filter": { 
      "_id": "1", 
      "option": { 
        "$elemMatch": { "weight": "40", "size": "40" }
      }
    },
    "update": { 
      "$set": { "option.$.price": "300" }
    }
  }},
  { "updateOne": {
    "filter": {
      "_id": "1",
      "option": {
        "$not": {
          "$elemMatch": { "weight": "40", "size": "40" }
        }
      }
    },
    "update": {
      "$push": { "option": { "weight": "40", "size": "40",  "price": "300" } }
    }
  }},
  { "updateOne": {
    "filter": { "_id": 1 },
    "update": {
      "$setOnInsert": {
        "option": [
           { "weight": "40", "size": "40",  "price": "300" }
         ]
      }
    },
    "upsert": true
  }}
])

So the operations are:

  1. Test that the array element matching conditions in $elemMatch is present and then $set the matched value.

  2. Test the array element is $not present in negation. You could alternately use $ne on each property, but negating the condition where both match is a bit cleaner.

     "$elemMatch": { "weight": { "$ne": "40" }, "size": { "$ne": "40" } }
    

    At any rate you $push the new array element when one not matching the supplied criteria is found.

  3. Attempt an "upsert" only where the primary document _id is not found, and use $setOnInsert so that if the document is found this operation does nothing.

Same as before, only one of these will actually write anything despite the whole batch being sent to the server.

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

5 Comments

This actually executes 3 operations instead of 1.
@AgostonHorvath Yes. But it's one request.You cannot do this in one operation, and reading the whole document and writing it back is just plain wrong for so many reasons.
When you have documents of this complexity, and then a request to do deep changes in there, for me, that's a red flag that your actual requirement is complex updates and validations. You can't do that in mongo, and sure as hell even if you do (like you suggested in your answer), it's a pain to maintain/change it later on. By doing the changes and validation in code, you have much better control and flexibility with much less and easier to understand code, with much better possibilities for testing.
it doesnot update the price if both weight and size r matching
@naruto It's the very first condition and an exact match for the data in the question. Check the data you are running against, or better yet actually run against the data you presented in the question. The only thing different from your previous question is $elemMatch. Read the linked documentation and understand that it is there to match "multiple conditions" just like I'm telling you it does.
0

You might be able to wire things together with a single, mongodb-only operation, you're probably better off by adding a 'version' field and using optimistic locking, like java+spring-data does it for you (google for @Version). That would allow for complex changes and validations of the full object before persistence.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.