Compare array with object field keys

Assuming that I have the following array a1:

["attr1", "attr2", "attr3"]

and document d1:

{
  "_id": ObjectId("640b0e3629cb7001946137a3")
  "field1": {
        "attr1": { /some data},
        "attr2": { /some data},
        "attr3": { /some data},
        "attr4": { /some data},
        "attr5": { /some data},
   },
  "field2": {
        "val": "some_val"
   }
}

How can I validate that each value in a1 has a field key in d1.field1 and based on the validation result update d1.field2.val2 in a single update query?

I tried the following query but it’s not working:

db.coll.update_one(
            {
                "_id": ObjectId('640b0e3629cb7001946137a3')
            },
            [
                {
                    "$set": {
                        "temp_f1": {"$objectToArray": "$fiedl1"}
                    }
                },
                {
                    "$set": {
                        "field2.val": {"$cond": [{"temp_f1.k": {"$all": a1}}, "UPDATED!", ""]}
                    }
                },
                {
                    "$unset": "temp_f1"
                },
            ]
        )

I’m new to MongoDB and any help is greatly appreciated.

Hello @loay_khateeb, Welcome to the MongoDB community forum,

Can you show how the document will look after the update? and a little bit of explanation would be easier to understand.

Hi @turivishal,

I want the document to be updated based on the following condition:

if all the values of a1 are included in the field1 keys then the value of of field2.val would be updated to Updated. a successful result would look like this:

{
  "_id": ObjectId("640b0e3629cb7001946137a3")
  "field1": {
        "attr1": { /some data},
        "attr2": { /some data},
        "attr3": { /some data},
        "attr4": { /some data},
        "attr5": { /some data},
   },
  "field2": {
        "val": "Updated"
   }
}

Otherwise, no changes should be made to the document.

I’m using the latest version of MongoDB (6.0.0) and the latest version of motor driver (3.1.1).
I would also like the UpdateResult.modified_count to be 0 in case the condition wasn’t met, if possible.

Thank you.

@loay_khateeb The relevant issue is to validate each value in a1 against the keys in d1.field1 and update d1.field2.val2 based on the validation result in a single update query. The provided query is not working, so an alternative solution is needed.
One solution can be to iterate over the values in a1 and check if they exist as keys in d1.field1. Then, based on the validation result, update d1.field2.val2. This can be achieved using the following query:

db.coll.updateOne(
  { "_id": ObjectId("640b0e3629cb7001946137a3") },
  [
     {
        "$addFields": {
           "temp_f1": { "$objectToArray": "$field1" }
        }
     },
     {
        "$set": {
           "field2.val": {
              "$cond": [
                 {
                    "$allElementsTrue": {
                       "$map": {
                          "input": ["attr1", "attr2", "attr3"],
                          "in": {
                             "$in": [
                                "$$this",
                                "$temp_f1.k"
                             ]
                          }
                       }
                    }
                 },
                 "UPDATED!",
                 ""
              ]
           }
        }
     },
     {
        "$unset": "temp_f1"
     }
  ]
)

The query first uses $objectToArray to convert field1 into an array of key-value pairs. Then, it creates a new field field2.val using $cond that checks if all elements in a1 are present in temp_f1.k, which is an array of keys in field1. If the condition is true, it sets the value of field2.val to "UPDATED!", otherwise, it sets it to an empty string. Finally, it uses $unset to remove the temp_f1 field.

le me know if this worked ?

@Deepak_Kumar16, the following

is exactly the same as the original post, except that you use $addFields in the first stage rather than $set. But they are equivalent.

As per your last post, I think you can do it in by normal update query, by checking are properties exist or not,

  • In condition, you can check $exists to check whether the property should be present by passing true
  • if the condition matches then it will go further for the update otherwise will return 0 in modified_count
db.coll.update_one({
  "_id": ObjectId("640b0e3629cb7001946137a3"),
  "field1.attr1": { $exists: true },
  "field1.attr2": { $exists: true },
  "field1.attr3": { $exists: true }
},
{
  $set: {
    "field2.val": "UPDATED!"
  }
})
1 Like

Thank you @turivishal, this is exactly what I needed.

I ended up using a for loop in python to prepare the second part of the filter query since the array a1 is dynamic but your query works just like I needed.

2 Likes

This topic was automatically closed 5 days after the last reply. New replies are no longer allowed.