Does the `$eq` operator work with array dot notation?

Viewed 289

I'm trying to write an aggregation query with $expr inside $lookup's $match pipeline stage. I found some problems related to the array dot notation.

Imagine a collection relations with the array field called nodes.

The following query works fine returning correct results:

db.relations.find({"nodes.0": { "type" : "User", "id" : UUID("dc20f7c7-bd45-4fc1-9eb4-3604428fa551") }})

But the one below returns nothing:

db.relations.find({$expr: {$and: [ {$eq: ["$nodes.0", { "type" : "User", "id" : UUID("dc20f7c7-bd45-4fc1-9eb4-3604428fa551") }]}]}})

To me they seem to be effectively the same, and I don't understand why the results differ. I tried to use the $arrayElemAt operator, it works, but unfortunately it triggers the COLLSCAN execution plan, which is undesirable, as I have an index on nodes.0.

I didn't find anything special about $eq operator in the docs, they even explicitly mention that those two expression forms are equivalent.

1 Answers

Does the $eq operator work with array dot notation?

No. $nodes.0 is not a correct expression in $eq aggregation operator, when used with array fields.

$eq aggregation operator has the following snytax:

{ $eq: [ <expression1>, <expression2> ] }

You will have to use $reduce along with $arrayElemAt to access your field in $eq operator:

Query1

db.relations.find({
  $expr: {
    $eq: [
      {
        $reduce: {
          input: [
            {
              $arrayElemAt: [
                "$nodes",
                0
              ]
            }
          ],
          initialValue: false,
          in: {
            "$and": [
              {
                $eq: [
                  "$$this.type",
                  "User"
                ]
              },
              {
                $eq: [
                  "$$this.id",
                  UUID("dc20f7c7-bd45-4fc1-9eb4-3604428fa551")
                ]
              }
            ]
          }
        }
      },
      true
    ]
  }
})

Playground

Alternative, using $eq operator { <field>: { $eq: <value> } }

Query2

db.relations.find({
  "$and": [
    {
      "nodes.0.type": {
        $eq: "User"
      }
    },
    {
      "nodes.0.id": {
        $eq: UUID("dc20f7c7-bd45-4fc1-9eb4-3604428fa551")
      }
    }
  ]
})

MongoDB Playground

If you want to perform IXSCAN, you can use Query2. You have to create the following indexes:

db.relations.ensureIndex({"nodes.0.id":1})
db.relations.ensureIndex({"nodes.0.type":1})
Related