|
Tess and I spent some time looking at this issue. One problem is that the addKeyMetadata member the IndexScanNode doesn't get set to true if the parsed projection specifies an index key meta projection.
The following patch makes it so that specifying an index key meta projection behaves identically to specifying $returnKey. This seems undesirable as we are effectively ignoring the field that index key meta projection was assigned to and only return the index entry. For example, the following query would return
> db.test.find({a:1}, {b: {$meta: "indexKey"}})
|
{ "a" : 1 }
|
rather than {b: {a: 1}}.
diff --git a/src/mongo/db/query/planner_access.cpp b/src/mongo/db/query/planner_access.cpp
|
index 54bcc0b..7f449d4 100644
|
--- a/src/mongo/db/query/planner_access.cpp
|
+++ b/src/mongo/db/query/planner_access.cpp
|
@@ -208,6 +208,9 @@ QuerySolutionNode* QueryPlannerAccess::makeLeafNode(
|
isn->bounds.fields.resize(index.keyPattern.nFields());
|
isn->maxScan = query.getParsed().getMaxScan();
|
isn->addKeyMetadata = query.getParsed().returnKey();
|
+ if (query.getProj() && query.getProj()->wantIndexKey()) {
|
+ isn->addKeyMetadata = true;
|
+ }
|
|
// Get the ixtag->pos-th element of the index key pattern.
|
// TODO: cache this instead/with ixtag->pos?
|
@@ -1238,6 +1241,9 @@ QuerySolutionNode* QueryPlannerAccess::scanWholeIndex(const IndexEntry& index,
|
isn->indexIsMultiKey = index.multikey;
|
isn->maxScan = query.getParsed().getMaxScan();
|
isn->addKeyMetadata = query.getParsed().returnKey();
|
+ if (query.getProj() && query.getProj()->wantIndexKey()) {
|
+ isn->addKeyMetadata = true;
|
+ }
|
|
IndexBoundsBuilder::allValuesBounds(index.keyPattern, &isn->bounds);
|
|
@@ -1375,6 +1381,10 @@ QuerySolutionNode* QueryPlannerAccess::makeIndexScan(const IndexEntry& index,
|
isn->direction = 1;
|
isn->maxScan = query.getParsed().getMaxScan();
|
isn->addKeyMetadata = query.getParsed().returnKey();
|
+ if (query.getProj() && query.getProj()->wantIndexKey()) {
|
+ isn->addKeyMetadata = true;
|
+ }
|
+
|
isn->bounds.isSimpleRange = true;
|
isn->bounds.startKey = startKey;
|
isn->bounds.endKey = endKey;
|
We also need the ProjectionExec code to be able to distinguish between $returnKey and {$meta: 'indexKey' in order to apply a FETCH stage if the projection specification includes fields other than those from the index key. With the indexkey_metaprojection.patch applied, I get the following the behavior when using an indexKey meta-projection.
> db.mycoll.drop();
|
> db.mycoll.insert({a: 1, c: 1});
|
> db.mycoll.createIndex({a: 1});
|
> db.mycoll.find({a: 1}, {_id: 0, a: 0, b: {$meta: "indexKey"}});
|
{ "c" : 1, "b" : { "a" : 1 } }
|
> db.mycoll.find({a: 1}, {_id: 0, a: 1, b: {$meta: "indexKey"}});
|
{ "a" : 1, "b" : { "a" : 1 } }
|
> db.mycoll.find({a: 1}, {_id: 0, c: 1, b: {$meta: "indexKey"}});
|
{ "c" : 1, "b" : { "a" : 1 } }
|
> db.mycoll.find({a: 1}, {_id: 0, c: 0, b: {$meta: "indexKey"}});
|
{ "a" : 1, "b" : { "a" : 1 } }
|
> db.mycoll.find({a: 1}, {_id: 0, b: {$meta: "indexKey"}});
|
{ "a" : 1, "c" : 1, "b" : { "a" : 1 } }
|
> db.mycoll.find({a: 1}, {_id: 1, b: {$meta: "indexKey"}});
|
{ "_id" : ObjectId("56b4f4b9268d1a261a80c6c0"), "b" : { "a" : 1 } }
|
> db.mycoll.find({a: 1}, {b: {$meta: "indexKey"}});
|
{ "_id" : ObjectId("56b4f4b9268d1a261a80c6c0"), "a" : 1, "c" : 1, "b" : { "a" : 1 } }
|
Unassigning this ticket from myself until cleanup of ProjectionExec happens as part of SERVER-20795.
|