[SERVER-56970] Invalid value for $expr used within $match pipeline stage is ignored Created: 14/May/21  Updated: 27/Oct/23  Resolved: 10/Jun/21

Status: Closed
Project: Core Server
Component/s: None
Affects Version/s: 5.0.0
Fix Version/s: None

Type: Bug Priority: Major - P3
Reporter: Jeremy Mikola Assignee: Charlie Swanson
Resolution: Works as Designed Votes: 0
Labels: None
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified

Operating System: ALL
Sprint: Query Optimization 2021-05-31, Query Optimization 2021-06-14
Participants:

 Description   

A user in mongodb/mongo-php-library#536 shared an aggregation pipeline with a $match stage and what appeared to be an invalid value for $expr. Quoting the aggregation expression docs:

Expressions can include field paths, literals, system variables, expression objects, and expression operators. Expressions can be nested.

I assume value expressions are strings and BSON documents. In the issue, the user was providing a BSON array.

I attempted to reproduce this locally with version 5.0.0-alpha0-282-g7b9eb05 (gitVersion: 7b9eb05ea26dd0adba9c097ba1fe68dc3c2e1ba1) and observed the following:

rs0:PRIMARY> db.foo.find()
{ "_id" : ObjectId("609eb06d9c3fd06ec84b2ead"), "x" : 1 }
rs0:PRIMARY> db.foo.aggregate([{$match:{$expr:[]}}])
{ "_id" : ObjectId("609eb06d9c3fd06ec84b2ead"), "x" : 1 }

I also tried some other values that I expected would be invalid:

rs0:PRIMARY> db.foo.aggregate([{$match:{$expr:{0:1}}}])
{ "_id" : ObjectId("609eb06d9c3fd06ec84b2ead"), "x" : 1 }
rs0:PRIMARY> db.foo.aggregate([{$match:{$expr:{foo:1}}}])
{ "_id" : ObjectId("609eb06d9c3fd06ec84b2ead"), "x" : 1 }

Using an unsupported operator (e.g. $foo) does result in an error. But in the examples above it looks like the expression was simply ignored. Is this expected behavior, or should the server be more strict about validating the expression value?



 Comments   
Comment by Charlie Swanson [ 10/Jun/21 ]

Hi jmikola. Although it's odd, this is working "as designed" I think. The examples {0: 1} and {foo: 1} are both valid expressions representing object literals. The $expr expression will use some definition of "truthiness", which considers any object as true, so any document will match the predicate you typed:

$expr's evaluate logic

        return evaluateExpression(doc).coerceToBool();

coerceToBool

    switch (getType()) {
        case CodeWScope:
        case MinKey:
        case DBRef:
        case Code:
        case MaxKey:
        case String:
        case Object:  // This is the case you're hitting.
        case Array:
        case BinData:
        case jstOID:
        case Date:
        case RegEx:
        case Symbol:
        case bsonTimestamp:
            return true;
 
        case EOO:
        case jstNULL:
        case Undefined:
            return false;
 
        case Bool:
            return _storage.boolValue;
        case NumberInt:
            return _storage.intValue;
        case NumberLong:
            return _storage.longValue;
        case NumberDouble:
            return _storage.doubleValue;
        case NumberDecimal:
            return !_storage.getDecimal().isZero();
    }
    verify(false);

Generated at Thu Feb 08 05:40:39 UTC 2024 using Jira 9.7.1#970001-sha1:2222b88b221c4928ef0de3161136cc90c8356a66.