Can I compare 2 objects on the same doc?

I am writing an aggregation pipeline, that will essentially have docs like this:

{
  obj1: {foo: 'bar'}, 
  obj2: {foo: 'baar'}
}

Can I somehow write a query to get all the documents, where obj1 and obj2 don’t match?

I tried with the $eq, but this operator does not seem to work.

Does both obj1 and obj2 have only the foo as sub-property? Do you want to try to match only foo sub-property or all sub-properties in case of multiple sub-properties?

The foo sub-property was only to make the example easy :sweat_smile:

My objective is to test all sub-properties.

I just found out that $eq actually works, but the order of keys inside an object is important!

I’m investigating my options…

Here is how you can do it:

  • $objectToArray - to convert objects to arrays
  • $setEquals - to compare arrays
db.collection.aggregate([
  {
    "$match": {
      "$expr": {
        "$setEquals": [
          {
            "$objectToArray": "$object_1"
          },
          {
            "$objectToArray": "$object_2"
          }
        ]
      }
    }
  }
])

Working example


Use case when one or both sub-properties are not of type “object”

Here is the updated answer that would also work if one or both sub-properties are not of type object:

  • $type - to check if both sub-properties are of the type `object.
  • If they are, do the comparation with $setEquals and objectToArray.
  • If they are not, do the comparation with $eq operator.
db.collection.aggregate([
  {
    "$match": {
      "$expr": {
        "$cond": {
          if: {
            "$and": [
              {
                "$eq": [
                  {
                    "$type": "$object_1"
                  },
                  "object"
                ]
              },
              {
                "$eq": [
                  {
                    "$type": "$object_2"
                  },
                  "object"
                ]
              }
            ]
          },
          then: {
            "$setEquals": [
              {
                "$objectToArray": "$object_1"
              },
              {
                "$objectToArray": "$object_2"
              }
            ]
          },
          else: {
            "$eq": [
              "$object_1",
              "$object_2"
            ]
          }
        }
      }
    }
  }
])

Working Example

4 Likes

Thanks - it actually works :+1:

Hi @NeNaD

We are starting to see some issues when comparing complex objects, with nested arrays and objects. Is $objectToArray capable of working with nested objects - or is there some work-around?

Hi @Alex_Bjorlig,

I am not sure. Can you add example documents?

If you have nested objects, maybe after $objectToArray it will have the same issue as when you just try to match 2 objects and keys are not in the same order. :thinking:

Issues seem to arrive when the 2 objects to compare looks like this:

{
  objectToCompare: {
    "foo": "bar",
    "fooLevels": {
      "1": "level 1",
      "2": "level 2",
      "3": "level 3"
    }
  }
}

But it actually seems like I can get it “to work” if I project fooLevels directly :thinking:

It does not work because in this case foo is not an object, it’s a string. And $objectToArray throws an error because it expects input of type object.

I’m sorry for not being explicit about the data structure. I updated the example…

Hi,

I updated my answer. Can you check it?