Reputation: 974
According to the docs, MongoDB should skip the FETCH stage when an index covers a query.
If I correctly understood, this sentences explain this behavior:
Covered Queries When an index covers a query, MongoDB can both match the query conditions and return the results using only the index keys; i.e. MongoDB does not need to examine documents from the collection to return the results.
When an index covers a query, the explain result has an IXSCAN stage that is not a descendant of a FETCH stage, and in the executionStats, the totalDocsExamined is 0.
In earlier versions of MongoDB, cursor.explain() returned the indexOnly field to indicate whether the index covered a query. (https://docs.mongodb.com/manual/reference/explain-results/)
And this
With this in place, the query takes less than 2 Ms. Because the index ‘covered’ the query, MongoDB was able to match the query conditions and return the results using only the index keys; without even needing to examine documents from the collection to return the results. (if you see an IXSCAN stage that is not a child of a FETCH stage, in the execution plan then the index ‘covered’ the query.) (https://studio3t.com/knowledge-base/articles/mongodb-index-strategy/)
But in a test scenario it doesn't occur:
Example to test:
db.Test.insert({"Field1":"data on field1: 1","Field2":"data on field2: 1"});
db.Test.insert({"Field1":"data on field1: 2","Field2":"data on field2: 2"});
db.Test.insert({"Field1":"data on field1: 3","Field2":"data on field2: 3"});
db.Test.insert({"Field1":"data on field1: 4","Field2":"data on field2: 4"});
db.Test.insert({"Field1":"data on field1: 5","Field2":"data on field2: 5"});
db.Test.insert({"Field1":"data on field1: 6","Field2":"data on field2: 6"});
After I created an index to Field2.
db.Test.createIndex({"Field2":1})
And then I query the collection:
db.Test.find({"Field2":"data on field2: 5"}).explain("executionStats");
I expected a stage IDXSCAN
that isn't child of a FETCH
stage. But the output is like this:
[...]
"winningPlan" : {
"stage" : "FETCH",
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"Campo2" : 1.0
},
"indexName" : "Field2_1",
"isMultiKey" : false,
"multiKeyPaths" : {
"Campo2" : []
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "forward",
"indexBounds" : {
"Field2" : [
"[\"data on field2: 5", \"data on field2: 5\"]"
]
}
}
},
[...]
There are two stages: one "stage" : "FETCH", and its child "stage" : "IXSCAN",.
Can anyone explain what I am misunderstanding?
*** ABOUT PROJECTION
When running query with projection
"winningPlan" : {
"stage" : "PROJECTION",
"transformBy" : {
"Campo2" : 1.0
},
"inputStage" : {
"stage" : "FETCH",
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"Field2" : 1.0
},
"indexName" : "Field2_1",
"isMultiKey" : false,
"multiKeyPaths" : {
"Campo2" : []
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "forward",
"indexBounds" : {
"Field2" : [
"[\"data on field2: 5", \"data on field2: 5\"]"
]
}
}
}
},
With Adam's answer: it worked!
I realized that projection shouldn't include "_id" to avoid FETCH
.
Upvotes: 3
Views: 7098
Reputation: 3421
Your query doesn't specify a projection, meaning that it will return all fields in the document. This means that the { Field2: 1 }
index does not cover the query, since it only contains a single field.
The following query should be fully covered and should not have a FETCH stage. Note that the projection is explicitly excluding the _id
field, as this will be included in the projection unless specified otherwise:
db.Test.find(
{"Field2":"data on field2: 5"},
{"Field2" : 1, "_id" : 0 }
).explain("executionStats");
Output:
{
"queryPlanner" : {
"plannerVersion" : 1,
"namespace" : "foo.Test",
"indexFilterSet" : false,
"parsedQuery" : {
"Field2" : {
"$eq" : "data on field2: 5"
}
},
"winningPlan" : {
"stage" : "PROJECTION",
"transformBy" : {
"Field2" : 1,
"_id" : 0
},
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"Field2" : 1
},
"indexName" : "Field2_1",
"isMultiKey" : false,
"multiKeyPaths" : {
"Field2" : [ ]
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "forward",
"indexBounds" : {
"Field2" : [
"[\"data on field2: 5\", \"data on field2: 5\"]"
]
}
}
},
"rejectedPlans" : [ ]
},
"executionStats" : {
"executionSuccess" : true,
"nReturned" : 1,
"executionTimeMillis" : 1,
"totalKeysExamined" : 1,
"totalDocsExamined" : 0,
"executionStages" : {
"stage" : "PROJECTION",
"nReturned" : 1,
"executionTimeMillisEstimate" : 0,
"works" : 2,
"advanced" : 1,
"needTime" : 0,
"needYield" : 0,
"saveState" : 0,
"restoreState" : 0,
"isEOF" : 1,
"invalidates" : 0,
"transformBy" : {
"Field2" : 1,
"_id" : 0
},
"inputStage" : {
"stage" : "IXSCAN",
"nReturned" : 1,
"executionTimeMillisEstimate" : 0,
"works" : 2,
"advanced" : 1,
"needTime" : 0,
"needYield" : 0,
"saveState" : 0,
"restoreState" : 0,
"isEOF" : 1,
"invalidates" : 0,
"keyPattern" : {
"Field2" : 1
},
"indexName" : "Field2_1",
"isMultiKey" : false,
"multiKeyPaths" : {
"Field2" : [ ]
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "forward",
"indexBounds" : {
"Field2" : [
"[\"data on field2: 5\", \"data on field2: 5\"]"
]
},
"keysExamined" : 1,
"seeks" : 1,
"dupsTested" : 0,
"dupsDropped" : 0,
"seenInvalidated" : 0
}
}
},
"serverInfo" : {
...
},
"ok" : 1,
...
}
Upvotes: 6