Uploaded image for project: 'Core Server'
  1. Core Server
  2. SERVER-31804

$expr rewrite optimization can lead to incorrect query results

    XMLWordPrintable

    Details

    • Backwards Compatibility:
      Fully Compatible
    • Operating System:
      ALL
    • Sprint:
      Query 2017-11-13

      Description

      The 3.6 stable release series adds support for $expr, a way to use the aggregation expression language within a $match predicate:

      https://docs.mongodb.com/master/reference/operator/query/expr/

      The query engine optimizes $expr by attempting to fully or partially convert the $expr into a MatchExpression which can then be used to generate index bounds in the query planner. However, this rewrite optimization is incorrect if the $expr expresses a match over a path that contains any array whatsoever, regardless of whether the path is dotted. Consider the following example:

      > db.c.drop();
      > db.c.insert({a: [1, 2, 3]});
      > db.c.find({$expr: {$gt: ["$a", 4]}});
      // No results. This is incorrect because the array [1, 2, 3] is considered greater than
      // the number 4 in the aggregation language. In fact, all arrays are considered greater
      // than all numbers in aggregation, since the first part of the comparison is the
      // canonical type.
       
      // The explain shows that the match expression {a: {$gt: 4}} has been generated by
      // the $expr rewrite optimization.
      > db.c.find({$expr: {$gt: ["$a", 4]}}).explain()
      {
      	"queryPlanner" : {
      		"plannerVersion" : 1,
      		"namespace" : "test.c",
      		"indexFilterSet" : false,
      		"parsedQuery" : {
      			"$and" : [
      				{
      					"a" : {
      						"$gt" : 4
      					}
      				},
      				{
      					"$expr" : {
      						"$gt" : [
      							"$a",
      							{
      								"$const" : 4
      							}
      						]
      					}
      				}
      			]
      		},
      		"winningPlan" : {
      			"stage" : "COLLSCAN",
      			"filter" : {
      				"$and" : [
      					{
      						"a" : {
      							"$gt" : 4
      						}
      					},
      					{
      						"$expr" : {
      							"$gt" : [
      								"$a",
      								{
      									"$const" : 4
      								}
      							]
      						}
      					}
      				]
      			},
      			"direction" : "forward"
      		},
      		"rejectedPlans" : [ ]
      	},
      	"serverInfo" : {
      		"host" : "storchbox",
      		"port" : 27017,
      		"version" : "0.0.0",
      		"gitVersion" : "unknown"
      	},
      	"ok" : 1
      }
      

      The essence of the problem is that the match language has implicit array traversal semantics whereas the aggregation expression language does not. {$gt: ["$a", 4]} and {a: {$gt: 4}} may look the same syntactically, but their meanings are not equivalent.

        Attachments

          Issue Links

            Activity

              People

              • Votes:
                0 Vote for this issue
                Watchers:
                8 Start watching this issue

                Dates

                • Created:
                  Updated:
                  Resolved: