When I read the document I found the following notes:
When a $sort immediately precedes a $limit in the pipeline, the $sort operation only maintains t
I found that it seems the sequence of limit and skip is immaterial. If I specify skip before limit, the mongoDB will make limit before skip under the hood.
> db.system.profile.find().limit(1).sort( { ts : -1 } ).pretty()
{
"op" : "command",
"ns" : "archiprod.userinfos",
"command" : {
"aggregate" : "userinfos",
"pipeline" : [
{
"$sort" : {
"updatedAt" : -1
}
},
{
"$limit" : 625
},
{
"$skip" : 600
}
],
},
"keysExamined" : 625,
"docsExamined" : 625,
"cursorExhausted" : true,
"numYield" : 4,
"nreturned" : 25,
"millis" : 25,
"planSummary" : "IXSCAN { updatedAt: -1 }",
/* Some fields are omitted */
}
What happens if I swtich $skip and $limit? I got the same result in terms of keysExamined and docsExamined.
> db.system.profile.find().limit(1).sort( { ts : -1 } ).pretty()
{
"op" : "command",
"ns" : "archiprod.userinfos",
"command" : {
"aggregate" : "userinfos",
"pipeline" : [
{
"$sort" : {
"updatedAt" : -1
}
},
{
"$skip" : 600
},
{
"$limit" : 25
}
],
},
"keysExamined" : 625,
"docsExamined" : 625,
"cursorExhausted" : true,
"numYield" : 5,
"nreturned" : 25,
"millis" : 71,
"planSummary" : "IXSCAN { updatedAt: -1 }",
}
I then checked the explain result of the query. I found that totalDocsExamined is already 625 in the limit stage.
> db.userinfos.explain('executionStats').aggregate([ { "$sort" : { "updatedAt" : -1 } }, { "$limit" : 625 }, { "$skip" : 600 } ])
{
"stages" : [
{
"$cursor" : {
"sort" : {
"updatedAt" : -1
},
"limit" : NumberLong(625),
"queryPlanner" : {
"winningPlan" : {
"stage" : "FETCH",
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"updatedAt" : -1
},
"indexName" : "updatedAt_-1",
}
},
},
"executionStats" : {
"executionSuccess" : true,
"nReturned" : 625,
"executionTimeMillis" : 22,
"totalKeysExamined" : 625,
"totalDocsExamined" : 625,
"executionStages" : {
"stage" : "FETCH",
"nReturned" : 625,
"executionTimeMillisEstimate" : 0,
"works" : 625,
"advanced" : 625,
"docsExamined" : 625,
"inputStage" : {
"stage" : "IXSCAN",
"nReturned" : 625,
"works" : 625,
"advanced" : 625,
"keyPattern" : {
"updatedAt" : -1
},
"indexName" : "updatedAt_-1",
"keysExamined" : 625,
}
}
}
}
},
{
"$skip" : NumberLong(600)
}
]
}
And surprisingly, I found switching the $skip and $limit results in the same explain result.
> db.userinfos.explain('executionStats').aggregate([ { "$sort" : { "updatedAt" : -1 } }, { "$skip" : 600 }, { "$limit" : 25 } ])
{
"stages" : [
{
"$cursor" : {
"sort" : {
"updatedAt" : -1
},
"limit" : NumberLong(625),
"queryPlanner" : {
/* Omitted */
},
"executionStats" : {
"executionSuccess" : true,
"nReturned" : 625,
"executionTimeMillis" : 31,
"totalKeysExamined" : 625,
"totalDocsExamined" : 625,
/* Omitted */
}
}
},
{
"$skip" : NumberLong(600)
}
]
}
As you can see, even though I specified $skip before $limit, in the explain result, it's still $limit before $skip.