0

I have following document on which the update needs to be done.


    {
        "_id": "Colorcode_1",
        "Combination": [
            {
                "color": [
                    {
                        "mixture": [
                            "Red",
                            "Green"
                        ]
                    }
                ],
                "code": "Maroon"
            },
            {
                "color": [
                    {
                        "mixture": [
                            "Yellow",
                            "Green"
                        ]
                    }
                ],
                "code": "Light Green"
            }
        ]
    }

Now what I need to do is to update the document by adding the value "Blue" in the "mixture" field where "code" is "Maroon". Something like this. This needs to be done using $addToSet


    {
        "_id": "Colorcode_1",
        "Combination": [
            {
                "color": [
                    {
                        "mixture": [
                            "Red",
                            "Green",
                            "Blue"
                        ]
                    }
                ],
                "code": "Maroon"
            },
            {
                "color": [
                    {
                        "mixture": [
                            "Yellow",
                            "Green"
                        ]
                    }
                ],
                "code": "Light Green"
            }
        ]
    }

Any help regarding this would be highly helpful.

2
  • Just wondering ... Why is "color" an array?
    – rickhg12hs
    Commented May 5, 2022 at 14:39
  • Above document is just then reference to the problem I am facing. So it is self made example. @rickhg12hs Commented May 5, 2022 at 15:08

2 Answers 2

3

Here is option with arrayFilters:

db.collection.update({
   "Combination.code": "Maroon"
  },
  {
   "$addToSet": {
    "Combination.$[x].color.$[y].mixture": "Blue"
   }
 },
 {
  arrayFilters: [
  {
    "x.code": "Maroon"
  },
  {
   "y.mixture": {
    $exists: true
  }
}
 ]
})

Explained:

  1. Filter all documents having code:Marron , good to have index on this field if collection is big
  2. Use arrayFilter x.code to add the array element to mixture if mixture exists ( identified by y arrayFilter)

playground

5
  • 1
    What happens if the "color" array has objects without the the "mixture" field?
    – rickhg12hs
    Commented May 5, 2022 at 17:04
  • 1
    Ah you are right , added one more $filter y to identify if mixture $exists
    – R2D2
    Commented May 5, 2022 at 17:06
  • 1
    Wow! This is excellent! I'll leave my answer as a lesson to myself and encouragement for all to learn more.
    – rickhg12hs
    Commented May 5, 2022 at 17:08
  • 2
    Yours also interesting and working , but abit complex ...
    – R2D2
    Commented May 5, 2022 at 17:10
  • 1
    Would be interesting to "look under the hood" and see the real differences between the two updates. E.g., is the server "compiling/optimizing" both updates to the same thing?
    – rickhg12hs
    Commented May 5, 2022 at 17:14
2

I found this update difficult because of the data model, and I'm hoping you'll get a better/simpler answer.

Anyway, here's one way you could do it. I would test this on more/different data to insure it's correct.

db.collection.update({
  "_id": "Colorcode_1",
  "Combination.code": "Maroon"
},
[
  {
    "$set": {
      "Combination": {
        "$map": {
          "input": "$Combination",
          "as": "elem",
          "in": {
            "$cond": [
              { "$eq": [ "$$elem.code", "Maroon" ] },
              {
                "$mergeObjects": [
                  "$$elem",
                  {
                    "color": {
                      "$map": {
                        "input": "$$elem.color",
                        "as": "colorElem",
                        "in": {
                          "$cond": [
                            {
                              "$reduce": {
                                "input": { "$objectToArray": "$$colorElem" },
                                "initialValue": false,
                                "in": {
                                  "$or": [
                                    "$$value",
                                    { "$eq": [ "$$this.k", "mixture" ] }
                                  ]
                                }
                              }
                            },
                            {
                              "mixture": {
                                "$setUnion": [ "$$colorElem.mixture", [ "Blue" ] ]
                              }
                            },
                            "$$colorElem"
                          ]
                        }
                      }
                    }
                  }
                ]
              },
              "$$elem"
            ]
          }
        }
      }
    }
  }
])

Try it on mongoplayground.net.

1
  • looks a lot going inside the query, but works Commented May 6, 2022 at 8:33

Not the answer you're looking for? Browse other questions tagged or ask your own question.