Problem with Project and Match array element with $expr

I have this simple test document

[
{
arry: [0,1],
d: 1
},
{
arry: [1,0],
d:0
}
]

I’d like to compare one array element to another. I tried using the $expr with dot notation and it didn’t work
db.col.find({$expr: {$gt: [‘$arry.0’, ‘$arry.1’]}})

Then I tried this and it worked
db.col.aggregate({$project: {all: "$$ROOT, a0: {$slice: [‘$arry’, 0, 1]}, a1: {$slice: [‘$arry’, 1,1]}}}, {$match: {$expr: {$lt: [‘$a0’, ‘$a1’]}}}])

But this failed and I don’t know why
db.col.aggregate({$project: {all: "$$ROOT, a0: {$slice: [‘$arry’, 0, 1]}, a1: {$slice: [‘$arry’, 1,1]}}}, {$match: {$expr: {$lt: [‘$a0’, ‘$all.d’]}}}])

I also tried using $arrayElemAt to extract the array elements but same result.

Please tell me what I’m doing wrong here. Thank you!

Please read Formatting code and log snippets in posts and then update your post.

Once we can easily read and cut-n-paste your code and documents we might be able to help.

My apologies for not formatting the code…

I have a simple test collection:

[
  {
    arry: [0,1],
    d: 1
  },
  {
    arry: [1,0],
    d: 0
  }
]


I’d like to compare one array element to another. I tried using the $expr with the dot notation but it didn’t work

db.collection.find({$expr: {$gt: ["$arry.0", "$arry.1"]}})

Then I tried the following. It worked but I’d like to understand why it behaves differently from the previous query

db.collection.aggregate([
  {
   $project: {all: "$$ROOT", 
             a0: {$slice: ["$arry", 0, 1]}, 
             a1: {$slice: ["$arry", 1,1]}
            }
  }, 
  {
   $match: {$expr: {$lt: ["$a0", "$a1"]}}
  }
])

In my usecase I also have a need to compare one array element to another field but why doesn’t the following work?

db.collection.aggregate([
  {
   $project: {all: "$$ROOT", 
             a0: {$slice: ["$arry", 0, 1]}, 
             a1: {$slice: ["$arry", 1,1]}
            }
  }, 
  {
   $match: {$expr: {$lt: ["$a0", "$all.d"]}}
  }
])

Hi Cuong,

In order to access a specific array element you need to use $arrayElemAt operator.

In the above code, $slice operator returns a subarray and the $lt operator then compares two subarrays so it eventually works as expected.

But in this code $lt operator now has to compare a array with a normal value so it fails.

The proper way of doing this would be -

2 Likes

Just to add an observation,

I think that

could be done more efficiently with

Unless you really need $$ROOT to be renamed all, but in your case you could $match with $d rather than $all.d. This is true especially if you have a $replaceRoot:{newRoot:$all} later in the real pipeline.

Thank you Steeve and Akshat!

Do you know why the following does not work?

db.collection.find({$expr: {$lt: ["$arry.0", "$arry.1"]}})

Please share sample documents for which you expect it to work but that it does not.

Looks like the dot notation does not work in this case. So

1 Like

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