Is this behaviour correct for aggregation string $lte?

Hi everyony

This is the secuence of commands:

db.c.insertOne({e:'2025-04-01'});
db.c.aggregate([{$match: { e: {"$lte": "2025-06-24"}}}]) // one match
db.c.aggregate([{ "$addFields": { "today": "2025-06-24"}},{$match: { e: {"$lte": "$today"}}}]) // no match
db.c.aggregate([{ "$addFields": { "today": "2025-06-24"}},{$match: { $expr: {"$lte":[ "$e", "$today"]}}}]) // one match

// instead of $lte when using $gte
db.c.aggregate([{$match: { e: {"$gte": "2025-01-01"}}}]) // one match
db.c.aggregate([{ "$addFields": { "first": "2025-01-01"}},{$match: { e: {"$gte": "$first"}}}]) // one match

Should $lte work as $gte does?

«The $lte compares both value and type, using the specified BSON comparison order for values of different types.»

both first, e and today are of type “string”:

db.c.aggregate([{ "$addFields": { "first": "2025-01-01"}},{$addFields: {c: {$type: "$first"}, d: {$type: "$e"} } }])

More reference:

it’s even stranger, look at this:

test> db.c.aggregate([{ "$addFields": { "today": "2025-06-24"}},{$match: { e: {"$lte": "$today"}}}]) // no match

test> db.c.aggregate([{ "$addFields": { "today": "2025-06-24"}},{$match: { today: {"$gte": "$e"}}}])
[
  {
    _id: ObjectId('685aab4050fc94e070d861e0'),
    e: '2025-04-01',
    today: '2025-06-24'
  }
]

To answer the question in the post title:

Yes the behaviour is correct.

The issue is that when you do

and

You are not comparing the value of the field e with the value of the fields today or first, despite using $. You are comparing the value of the field e with the strings $today and $first.

This is different when using $expr. This is why you get different results in

It just happens that “2025-01-01” is indeed greater than the string $first.

1 Like

this is :exploding_head: to me. Thanks for your answer.

2 Likes

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