I'm trying to figure out how to do pagination on a large collection with MongoDB using the C# driver. I tried using skip
and limit
. It works, but gets slower the more documents I'm skipping, which is an expected result after searching Google about this.
The C# driver also supports cursors, but I could not find anything on how to use cursors inside a REST-API to serve the frontend with data.
Is it possible to have a REST-API use cursors for pagination so that if I click "Next page" I get the next X results?
Edit:
As @ĐĵΝιΓΞΗΛψΚ mentioned, I may be missing indexes. So here is what the query looks like:
db.collection.find({
"start": {
"$gte": ISODate("2018-03-31T22:00:00Z"),
"$lte": ISODate("2018-04-30T22:00:00Z")
},
"mat": { "$in": ["XYZ"] },
"serial": { "$in": ["72398472"] },
"code": { "$in": ["5345"] },
"status": { "$in": ["OK"] }
})
.limit(500)
.explain("executionStats")
This is very fast because it used the index {serial: 1}
and only returns 21 results.
But start
, mat
, serial
, code
and status
are optional, so it could also look like this:
db.collection.find({
"start": {
"$gte": ISODate("2018-03-31T22:00:00Z"),
"$lte": ISODate("2018-04-30T22:00:00Z")
},
"mat": { "$in": ["XYZ"] },
"status": { "$in": ["OK"] }
})
.limit(500)
.explain("executionStats")
I was trying to figure out indexes to make these queries faster, so I currently have the following:
{ _id_: 1}
{serial: 1}
{mat: 1}
{start: 1}
{code: 1}
{status: 1}
{start: 1, mat: 1, serial: 1, code: 1, status: 1}
{start: 1, mat: 1}
(which should be covered by the before, so I think I could remove it){start: 1, code: 1}
{start: 1, serial: 1}
Also, I tested the following query:
db.collection.find({
"start" : {
"$gte" : ISODate("2018-05-01T00:00:00.000Z"),
"$lte" : ISODate("2018-05-31T00:00:00.000Z")
},
"mat": { "$in" : ["XYZ"] }
})
which took 757549 ms because it used the index {mat: 1}
instead of {start: 1, mat: 1}
(5725ms).
Edit 2:
Heres the output of .explain("executionStats")
:
{
"queryPlanner" : {
"plannerVersion" : 1,
"namespace" : "db.runs",
"indexFilterSet" : false,
"parsedQuery" : {
"$and" : [
{
"mat" : {
"$eq" : "1101024071"
}
},
{
"start" : {
"$lte" : ISODate("2018-05-01T00:00:00.000+02:00")
}
},
{
"start" : {
"$gte" : ISODate("2018-04-01T00:00:00.000+02:00")
}
}
]
},
"winningPlan" : {
"stage" : "LIMIT",
"limitAmount" : 500,
"inputStage" : {
"stage" : "FETCH",
"filter" : {
"$and" : [
{
"start" : {
"$lte" : ISODate("2018-05-01T00:00:00.000+02:00")
}
},
{
"start" : {
"$gte" : ISODate("2018-04-01T00:00:00.000+02:00")
}
}
]
},
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"mat" : 1
},
"indexName" : "mat",
"isMultiKey" : false,
"multiKeyPaths" : {
"mat" : [ ]
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "forward",
"indexBounds" : {
"mat" : [
"[\"1101024071\", \"1101024071\"]"
]
}
}
}
},
"rejectedPlans" : [
{
"stage" : "LIMIT",
"limitAmount" : 500,
"inputStage" : {
"stage" : "FETCH",
"filter" : {
"mat" : {
"$eq" : "1101024071"
}
},
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"start" : 1
},
"indexName" : "start",
"isMultiKey" : false,
"multiKeyPaths" : {
"start" : [ ]
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "forward",
"indexBounds" : {
"start" : [
"[new Date(1522533600000), new Date(1525125600000)]"
]
}
}
}
},
{
"stage" : "LIMIT",
"limitAmount" : 500,
"inputStage" : {
"stage" : "FETCH",
"filter" : {
"mat" : {
"$eq" : "1101024071"
}
},
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"start" : 1,
"end" : 1
},
"indexName" : "start_end",
"isMultiKey" : false,
"multiKeyPaths" : {
"start" : [ ],
"end" : [ ]
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "forward",
"indexBounds" : {
"start" : [
"[new Date(1522533600000), new Date(1525125600000)]"
],
"end" : [
"[MinKey, MaxKey]"
]
}
}
}
},
{
"stage" : "LIMIT",
"limitAmount" : 500,
"inputStage" : {
"stage" : "FETCH",
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"start" : 1,
"mat" : 1
},
"indexName" : "start_1_mat_1",
"isMultiKey" : false,
"multiKeyPaths" : {
"start" : [ ],
"mat" : [ ]
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "forward",
"indexBounds" : {
"start" : [
"[new Date(1522533600000), new Date(1525125600000)]"
],
"mat" : [
"[\"1101024071\", \"1101024071\"]"
]
}
}
}
},
{
"stage" : "LIMIT",
"limitAmount" : 500,
"inputStage" : {
"stage" : "FETCH",
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"start" : 1,
"end" : 1,
"mat" : 1,
"serial" : 1,
"shipping_code" : 1,
"status" : 1
},
"indexName" : "start_end_mat_serial_code_status",
"isMultiKey" : false,
"multiKeyPaths" : {
"start" : [ ],
"end" : [ ],
"mat" : [ ],
"serial" : [ ],
"shipping_code" : [ ],
"status" : [ ]
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "forward",
"indexBounds" : {
"start" : [
"[new Date(1522533600000), new Date(1525125600000)]"
],
"end" : [
"[MinKey, MaxKey]"
],
"mat" : [
"[\"1101024071\", \"1101024071\"]"
],
"serial" : [
"[MinKey, MaxKey]"
],
"shipping_code" : [
"[MinKey, MaxKey]"
],
"status" : [
"[MinKey, MaxKey]"
]
}
}
}
},
{
"stage" : "LIMIT",
"limitAmount" : 500,
"inputStage" : {
"stage" : "FETCH",
"filter" : {
"mat" : {
"$eq" : "1101024071"
}
},
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"start" : 1,
"shipping_code" : 1
},
"indexName" : "start_1_code_1",
"isMultiKey" : false,
"multiKeyPaths" : {
"start" : [ ],
"shipping_code" : [ ]
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "forward",
"indexBounds" : {
"start" : [
"[new Date(1522533600000), new Date(1525125600000)]"
],
"shipping_code" : [
"[MinKey, MaxKey]"
]
}
}
}
},
{
"stage" : "LIMIT",
"limitAmount" : 500,
"inputStage" : {
"stage" : "FETCH",
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"start" : 1,
"mat" : 1,
"serial" : 1,
"shipping_code" : 1,
"status" : 1
},
"indexName" : "start_mat_serial_code_status",
"isMultiKey" : false,
"multiKeyPaths" : {
"start" : [ ],
"mat" : [ ],
"serial" : [ ],
"shipping_code" : [ ],
"status" : [ ]
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "forward",
"indexBounds" : {
"start" : [
"[new Date(1522533600000), new Date(1525125600000)]"
],
"mat" : [
"[\"1101024071\", \"1101024071\"]"
],
"serial" : [
"[MinKey, MaxKey]"
],
"shipping_code" : [
"[MinKey, MaxKey]"
],
"status" : [
"[MinKey, MaxKey]"
]
}
}
}
},
{
"stage" : "LIMIT",
"limitAmount" : 500,
"inputStage" : {
"stage" : "FETCH",
"filter" : {
"mat" : {
"$eq" : "1101024071"
}
},
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"start" : 1,
"serial" : 1,
"status" : 1
},
"indexName" : "start_serial",
"isMultiKey" : false,
"multiKeyPaths" : {
"start" : [ ],
"serial" : [ ],
"status" : [ ]
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "forward",
"indexBounds" : {
"start" : [
"[new Date(1522533600000), new Date(1525125600000)]"
],
"serial" : [
"[MinKey, MaxKey]"
],
"status" : [
"[MinKey, MaxKey]"
]
}
}
}
}
]
},
"executionStats" : {
"executionSuccess" : true,
"nReturned" : 500,
"executionTimeMillis" : 633514,
"totalKeysExamined" : 104446,
"totalDocsExamined" : 104446,
"executionStages" : {
"stage" : "LIMIT",
"nReturned" : 500,
"executionTimeMillisEstimate" : 181937,
"works" : 104447,
"advanced" : 500,
"needTime" : 103946,
"needYield" : 0,
"saveState" : 34932,
"restoreState" : 34932,
"isEOF" : 1,
"limitAmount" : 500,
"inputStage" : {
"stage" : "FETCH",
"filter" : {
"$and" : [
{
"start" : {
"$lte" : ISODate("2018-05-01T00:00:00.000+02:00")
}
},
{
"start" : {
"$gte" : ISODate("2018-04-01T00:00:00.000+02:00")
}
}
]
},
"nReturned" : 500,
"executionTimeMillisEstimate" : 181914,
"works" : 104446,
"advanced" : 500,
"needTime" : 103946,
"needYield" : 0,
"saveState" : 34932,
"restoreState" : 34932,
"isEOF" : 0,
"docsExamined" : 104446,
"alreadyHasObj" : 0,
"inputStage" : {
"stage" : "IXSCAN",
"nReturned" : 104446,
"executionTimeMillisEstimate" : 315,
"works" : 104446,
"advanced" : 104446,
"needTime" : 0,
"needYield" : 0,
"saveState" : 34932,
"restoreState" : 34932,
"isEOF" : 0,
"keyPattern" : {
"mat" : 1
},
"indexName" : "mat",
"isMultiKey" : false,
"multiKeyPaths" : {
"mat" : [ ]
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "forward",
"indexBounds" : {
"mat" : [
"[\"1101024071\", \"1101024071\"]"
]
},
"keysExamined" : 104446,
"seeks" : 1,
"dupsTested" : 0,
"dupsDropped" : 0,
"indexDef" : {
"indexName" : "mat",
"isMultiKey" : false,
"multiKeyPaths" : {
"mat" : [ ]
},
"keyPattern" : {
"mat" : 1
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"direction" : "forward"
}
}
}
}
},
"serverInfo" : {
"host" : "localhost",
"port" : 27017,
"version" : "4.2.13",
"gitVersion" : "82dd40f60c55dae12426c08fd7150d79a0e28e23"
},
"ok" : 1,
"$clusterTime" : {
"clusterTime" : Timestamp(1620838429, 122),
"signature" : {
"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
"keyId" : 0
}
},
"operationTime" : Timestamp(1620838429, 122)
}