projjal.chanda@mongodb.com: The issue here is how we deal with sort with raw bson sort order containing meta expressions.
For example for sort pattern { $sort: { score:
{ $meta: "textScore" }, posts: -1 } }, the “score” fieldname doesn’t mean anything. The parsed SortPattern doesn’t store it and when we pushdown sort from Agg layer to Find layer or during sort key merging, we reserialize it and replace the fieldname “score” with “$computed0”, which results in the mentioned error during project pushdown since the code treats it as a normal field instead of a special meta expression component.
Actual parsed sort pattern handling is ok since we don’t store this field path for meta expression case, and will be forced to deal with the meta expression separately. The issue happens when we deal with the raw BSONObj sort pattern instead. Apart from project pushdown, I audited other usages of raw bson sort order in the code base and we are not handling this in other places as well. Eg, I was able to reproduce the same error in a sharded cluster due to the reserialized sort key merge with a different edge case query.
However “$computed” will atmost cause the mentioned uassert error. But the more concerning situation is when this field name is not replaced by “$computed” field but uses the original field name (when we are using find command instead of aggregate and it is not reserialized) and the planner analysis treats it as a normal field and acts based on that.
I was able to cause a crash by triggering an invariant through the following script.
db.coll.insert({x: 1});
db.coll.createIndex({x: 1});
db.coll.find({x: {$gte: 1}}).sort({x: {$meta: "randVal”}});
This happens on analyzeSort call when checking for isSortCovered and because the field name for the sort order here is deceptively called “x” it passes the check.
Similarly in query encoder, we don’t handle the meta expression in encodeKeyForSort except only for “textScore” (valid $meta sort are "textScore", "randVal", "geoNearDistance", "searchScore", "vectorSearchScore", "score") so two different queries can be producing the same plan cache key. Running “db.coll.find({x: {$gte: 1}}).sort({y: {$meta: "randVal"}}).explain()” and “db.coll.find({x: {$gte: 1}}).sort(
{y: -1}).explain()” both give the same planCacheKey. (When reconstructing the plan from the cache, sort pattern is re-applied from the live FindCommandRequest so it still returns correct results)
The underlying issue has been there in mongo since the beginning so this can be reproduced in all the earlier versions (tested upto v6.0).
For a broader fix to prevent future issues, we should be parsing the SortPattern as early as possible and use that everywhere and avoid using the raw BSON obj.
- related to
-
SERVER-119464 Aggregation pipeline with $match: { $text }, $sort, $project gives an error
-
- In Code Review
-