skip to Main Content

I have two array of objects i my documents.

{
  "profileSpherestatus": [
    {
      "idf": "B002",
      "completedPercentage": 100
    },
    {
      "idf": "B003",
      "completedPercentage": 90
    },
    {
      "idf": "B004",
      "completedPercentage": 100
    }
  ]
  "myGratificacions": [
    {
      "idb": "B003",
      "gratification": 20
    },
    {
      "idb": "B004",
      "gratification": 30
    }
  ]
}

I want to add a new object in myGratifications if there is at least one object in the profileSpherestatus array where idf is "B002" and completedPercentage is 100, and there is not a gratification with "idb":"B002"

The newobject is:

newObject = {
  "idb":"B002",
  "gratification":10
}

This is my pipeline:

[
  {"$set": {
    "myGratifications":
      {"$cond": {
        "if": {
          "$and": [
            {"profileSpherestatus": 
              {"$elemMatch": {"idf": "B002", "completedPercentage": 100}}
            },
            {"$not": 
              {"myGratifications":{"$elemMatch": {"idb": "B002"}}}
            }
          ]
        },
        "then": {
            "$concatArrays": ["$myGratifications",[newObject]]
        },
        "else": "$myGratifications"
    }}
  }} 
]

But $elemMatch cannot be used inside $set or $addFields. Can you help me?

2

Answers


  1. $elemMatch is not supported when you use an aggregation pipeline.

    • Replace the first $elemMatch condition by checking the $size of the $filter array.

    • Replace the second $elemMatch condition with $not $in.

    db.collection.update({ /* match criteria */ },
    [
      {
        "$set": {
          "myGratificacions": {
            "$cond": {
              "if": {
                "$and": [
                  {
                    $gt: [
                      {
                        $size: {
                          $filter: {
                            input: "$profileSpherestatus",
                            cond: {
                              $and: [
                                {
                                  $eq: [
                                    "$$this.idf",
                                    "B002"
                                  ]
                                },
                                {
                                  $eq: [
                                    "$$this.completedPercentage",
                                    100
                                  ]
                                }
                              ]
                            }
                          }
                        }
                      },
                      0
                    ]
                  },
                  {
                    "$not": {
                      $in: [
                        "B002",
                        "$myGratificacions.idb"
                      ]
                    }
                  }
                ]
              },
              "then": {
                "$concatArrays": [
                  "$myGratificacions",
                  [
                    {
                      "idb": "B002",
                      "gratification": 10
                    }
                  ]
                ]
              },
              "else": "$myGratificacions"
            }
          }
        }
      }
    ])
    

    Demo @ Mongo Playground

    Note that there is a typo error in your current update query, myGratifications field doesn’t exist in your document, should be myGratificacions.

    Login or Signup to reply.
  2. If you are only doing this one update – adding the new element to myGratifications – then you can add your update-only-if conditions in the filter/match query for update.

    Combine the element checks with any other query you already have, like on _id, etc. In the update part, you can use the $push array update operator to add the new element.

    It has the added benefit of not doing any update when the document is found but the arrays don’t meet the criteria you’ve defined. May not make much of a difference for a single document update but if you wanted to update a thousands/millions, then documents which exist but don’t meet the criteria would be skipped.

    db.collection.update({
      // match criteria
      "$and": [
        // other filter criteria START
        {
          // as an example
          "_id": { "$regex": ".*" }
        },
        // other filter criteria END
        // ACTUAL filter criteria to update or not
        {
          "profileSpherestatus": {
            "$elemMatch": {
              "idf": "B002",
              "completedPercentage": 100
            }
          }
        },
        {
          "myGratifications": {
            "$not": {
              "$elemMatch": { "idb": "B002" }
            }
          }
        }
      ]
    },
    {
      // update operation
      "$push": {
        "myGratifications": { "idb": "B002", "gratification": 10 }
      }
    },
    { multi: true }
    )
    

    Mongo Playground with additional test docs


    Btw, you can still use your current set+concat method for the update part if you want, or have other $set operations to do. The match/filter query remains the same as above & the update part is similar to your existing then clause:

    [
      // update operations
      {
        "$set": {
          "myGratifications": {
            "$concatArrays": [
              "$myGratifications", [{ "idb": "B002", "gratification": 10 }]
            ]
          }
        }
      }
    ]
    

    mongo playground

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search