Does "$eq" aggregation operator works with array dot notation?

My MongoDB document schema looks like this:

[
  {
    "id": 1,
    "data": [
      {
        "val1": "xyz",
        "val2": "abc"
      },
      {
        "val1": "a",
        "val2": "b"
      }
    ]
  },
  {
    "id": 2,
    "data": [
      {
        "val1": "d",
        "val2": "e"
      },
      {
        "val1": "f",
        "val2": "f"
      }
    ]
  },
  
]

I need to find documents with data.0.val1="xyz" and data.0.val2="abc" and following is my query which works fine:

db.collection.find({
  "$and": [
    {
      "data.0.val1": {
        $eq: "xyz"
      }
    },
    {
      "data.0.val2": {
        $eq: "abc"
      }
    }
  ]
})

Playground

But when I tried using $eq aggregation operator along with array dot notation, it is not returning expected results:

db.collection.find({
  $expr: {
    $and: [
      {
        $eq: [
          "$data.0.val1",
          "xyz"
        ]
      },
      {
        $eq: [
          "$data.0.val2",
          "abc"
        ]
      }
    ]
  }
})

Playground

Does $eq aggregation operator works with array dot notation in find() ? What am I doing wrong?

First, $and is redundant because there is an implicit and in the query.

Second, $eq is also redundant because FieldName : Value implicitly mean equality.

I would try with simply

­­ db.collection.find( { "data.0.val1" : "xyz" , "data.0.val2" : "abc" } ) ­­

or (which I find easier to read)

­­ db.collection.find( { "data.0" : { "val1" : "xyz" , "val2" : "abc" } ) ­­

Thank you for the answer. But I was wondering How to use $eq aggregation operator along with array dot notation?

I think, "$data.0.val1" is not a correct expression in aggregation, when used with array fields. To get the data array’s first element’s val1 value, you need something like this using the aggregation operator $arrayElemAt. For example,

db.test.aggregate([ { $project: { first_ele: { $arrayElemAt: [ "$data", 0 ] } } } ])

returns two documents with first element of the array:

{ "_id" : 1, "first_ele" : { "val1" : "xyz", "val2" : "abc" } }
{ "_id" : 2, "first_ele" : { "val1" : "d", "val2" : "e" } }

Then, you can match with a specific field within the first_ele:
{ $match: { "first_ele.val1": "xyz" } }

This returns the document with _id: 1, the expected result.

So, the aggregation with both stages would be:

db.test.aggregate([
  { $addFields: { first_ele: { $arrayElemAt: [ "$data", 0 ] } } },
  { $match: { "first_ele.val1": "xyz" } }
])

Alternative, using $match with $expr:

db.test.aggregate([
  { $match: { 
      $expr: {
          $eq: [ { 
              $reduce: { 
                   input: [ { $arrayElemAt: [ "$data", 0 ] } ], 
                   initialValue: false,
                   in: { $eq: [ "$$this.val1", "xyz" ] }
              } }, 
              true 
          ]
      }
  } }
]).pretty()

This query with the $expr used with find works fine:

db.test.find( { 
  $expr: { 
    $eq: [ { 
        $reduce: { 
            input: [ { $arrayElemAt: [ "$data", 0 ] } ], 
            initialValue: false,
            in: { $eq: [ "$$this.val1", "xyz" ] }
        } }, 
        true 
    ]
  } 
}).pretty()
1 Like

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