Why a covered count query is slow? - mongodb

I'm running a mongodb v3.2.12 sharded cluster. Shard key is _id which is a md5 hash.
The problem is, that a covered count query takes much time.
The used index is about 5 GB on each mongodb node. The total size of all indexes is 32 GB and fits perfectly in RAM since each node has 128 GB RAM.
The query is: db.offer.count({ "shopId": 275419, "missingSince": null})
The used index has been created as: db.offer.createIndex({shopId:1, missingSince:1, merchantId:1, _id:1}, {background:true})
As you can see, the index is not sparse, so even null values are present in the index.
Running db.currentOp() while the query is running shows that the query is using the right index, However, it is running for over 2814 seconds already:
{
"desc" : "conn56062",
"threadId" : "140131556767488",
"connectionId" : 56062,
"client_s" : "x.x.x.x:39177",
"active" : true,
"opid" : "offerStoreIT02:1075309911",
"secs_running" : 2814,
"microsecs_running" : NumberLong("2814791918"),
"op" : "command",
"ns" : "offerStore.offer",
"query" : {
"query" : {
"count" : "offer",
"query" : {
"missingSince" : null,
"shopId" : 275419
}
},
"$readPreference" : {
"mode" : "primaryPreferred"
}
},
"planSummary" : "IXSCAN { shopId: 1.0, missingSince: 1.0, merchantId: 1.0, _id: 1.0 }",
"numYields" : 249244,
"locks" : {
"Global" : "r",
"Database" : "r",
"Collection" : "r"
},
"waitingForLock" : false,
"lockStats" : {
"Global" : {
"acquireCount" : {
"r" : NumberLong(498490)
}
},
"Database" : {
"acquireCount" : {
"r" : NumberLong(249245)
}
},
"Collection" : {
"acquireCount" : {
"r" : NumberLong(249245)
}
}
}
}
Iterating a 5 GB in-memory index never takes so much time. While the query is running, each mongodb primary is reading constantly 75-100 MB/sec from disk. When the query is not running, only 5-10 MB/sec are read from disk, so my assumption is that mongodb fetches the documents from SSD into memory in order to count them.
But why is it so? The query should be covered by the index since all fields, including the shardkey, is present in the index, which should be sufficient to cover the query according to mongodb documentation:
https://docs.mongodb.com/manual/core/query-optimization/#covered-queries
Follow-up:
I broke down the problem to a minimalistic, unsharded setup. I inserted the following types of documents:
a) 3 documents without both fields shopId and missingSince
b) 5 documents with field shopId:1 without field missingSince
c) 7 documents with field shopId:1 and missingSince:null
d) 13 documents with field shopId:1 and missingSince:ISODate("2017-05-22T07:52:40.831Z")
I created the index {shopId:1, missingSince:1}.
The execution plan of the query count({"shopId":1, "missingSince":null}) indicated "totalDocsExamined" : 12 which means that 12 documents had to be fetched. These must be the 5 documents of b) plus the 7 documents of c). All these 12 documents should be in the index with shopId:1, missingSince:null, thus satisfying the query.
But why does mongodb still need to fetch and examine these 12 documents?
Here is my test collection:
rs1:PRIMARY> db.offer.find()
{ "_id" : 1, "v" : 1 }
{ "_id" : 2, "v" : 1 }
{ "_id" : 3, "v" : 1 }
{ "_id" : 4, "shopId" : 1, "v" : 1 }
{ "_id" : 5, "shopId" : 1, "v" : 1 }
{ "_id" : 6, "shopId" : 1, "v" : 1 }
{ "_id" : 7, "shopId" : 1, "v" : 1 }
{ "_id" : 8, "shopId" : 1, "v" : 1 }
{ "_id" : 9, "shopId" : 1, "missingSince" : null, "v" : 1 }
{ "_id" : 10, "shopId" : 1, "missingSince" : null, "v" : 1 }
{ "_id" : 11, "shopId" : 1, "missingSince" : null, "v" : 1 }
{ "_id" : 12, "shopId" : 1, "missingSince" : null, "v" : 1 }
{ "_id" : 13, "shopId" : 1, "missingSince" : null, "v" : 1 }
{ "_id" : 14, "shopId" : 1, "missingSince" : null, "v" : 1 }
{ "_id" : 15, "shopId" : 1, "missingSince" : null, "v" : 1 }
{ "_id" : 16, "shopId" : 1, "missingSince" : ISODate("2017-05-22T07:52:40.831Z"), "v" : 1 }
{ "_id" : 17, "shopId" : 1, "missingSince" : ISODate("2017-05-22T07:52:40.831Z"), "v" : 1 }
{ "_id" : 18, "shopId" : 1, "missingSince" : ISODate("2017-05-22T07:52:40.831Z"), "v" : 1 }
{ "_id" : 19, "shopId" : 1, "missingSince" : ISODate("2017-05-22T07:52:40.831Z"), "v" : 1 }
{ "_id" : 20, "shopId" : 1, "missingSince" : ISODate("2017-05-22T07:52:40.831Z"), "v" : 1 }
{ "_id" : 21, "shopId" : 1, "missingSince" : ISODate("2017-05-22T07:52:40.831Z"), "v" : 1 }
{ "_id" : 22, "shopId" : 1, "missingSince" : ISODate("2017-05-22T07:52:40.831Z"), "v" : 1 }
{ "_id" : 23, "shopId" : 1, "missingSince" : ISODate("2017-05-22T07:52:40.831Z"), "v" : 1 }
{ "_id" : 24, "shopId" : 1, "missingSince" : ISODate("2017-05-22T07:52:40.831Z"), "v" : 1 }
{ "_id" : 25, "shopId" : 1, "missingSince" : ISODate("2017-05-22T07:52:40.831Z"), "v" : 1 }
{ "_id" : 26, "shopId" : 1, "missingSince" : ISODate("2017-05-22T07:52:40.831Z"), "v" : 1 }
{ "_id" : 27, "shopId" : 1, "missingSince" : ISODate("2017-05-22T07:52:40.831Z"), "v" : 1 }
{ "_id" : 28, "shopId" : 1, "missingSince" : ISODate("2017-05-22T07:52:40.831Z"), "v" : 1 }
Here is the output of explain():
rs1:PRIMARY> db.offer.explain(true).count({"shopId":1, "missingSince":null})
{
"queryPlanner" : {
"plannerVersion" : 1,
"namespace" : "test.offer",
"indexFilterSet" : false,
"parsedQuery" : {
"$and" : [
{
"missingSince" : {
"$eq" : null
}
},
{
"shopId" : {
"$eq" : 1
}
}
]
},
"winningPlan" : {
"stage" : "COUNT",
"inputStage" : {
"stage" : "FETCH",
"filter" : {
"missingSince" : {
"$eq" : null
}
},
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"shopId" : 1,
"missingSince" : 1
},
"indexName" : "shopId_1_missingSince_1",
"isMultiKey" : false,
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 1,
"direction" : "forward",
"indexBounds" : {
"shopId" : [
"[1.0, 1.0]"
],
"missingSince" : [
"[null, null]"
]
}
}
}
},
"rejectedPlans" : [ ]
},
"executionStats" : {
"executionSuccess" : true,
"nReturned" : 0,
"executionTimeMillis" : 0,
"totalKeysExamined" : 12,
"totalDocsExamined" : 12,
"executionStages" : {
"stage" : "COUNT",
"nReturned" : 0,
"executionTimeMillisEstimate" : 0,
"works" : 13,
"advanced" : 0,
"needTime" : 12,
"needYield" : 0,
"saveState" : 0,
"restoreState" : 0,
"isEOF" : 1,
"invalidates" : 0,
"nCounted" : 12,
"nSkipped" : 0,
"inputStage" : {
"stage" : "FETCH",
"filter" : {
"missingSince" : {
"$eq" : null
}
},
"nReturned" : 12,
"executionTimeMillisEstimate" : 0,
"works" : 13,
"advanced" : 12,
"needTime" : 0,
"needYield" : 0,
"saveState" : 0,
"restoreState" : 0,
"isEOF" : 1,
"invalidates" : 0,
"docsExamined" : 12,
"alreadyHasObj" : 0,
"inputStage" : {
"stage" : "IXSCAN",
"nReturned" : 12,
"executionTimeMillisEstimate" : 0,
"works" : 13,
"advanced" : 12,
"needTime" : 0,
"needYield" : 0,
"saveState" : 0,
"restoreState" : 0,
"isEOF" : 1,
"invalidates" : 0,
"keyPattern" : {
"shopId" : 1,
"missingSince" : 1
},
"indexName" : "shopId_1_missingSince_1",
"isMultiKey" : false,
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 1,
"direction" : "forward",
"indexBounds" : {
"shopId" : [
"[1.0, 1.0]"
],
"missingSince" : [
"[null, null]"
]
},
"keysExamined" : 12,
"dupsTested" : 0,
"dupsDropped" : 0,
"seenInvalidated" : 0
}
}
},
"allPlansExecution" : [ ]
},
"serverInfo" : {
"host" : "Kays MacBook Pro",
"port" : 27017,
"version" : "3.2.6",
"gitVersion" : "05552b562c7a0b3143a729aaa0838e558dc49b25"
},
"ok" : 1
}

Since nobody could find a valid reason for this issue, I opened yesterday a mongodb bug report: https://jira.mongodb.org/browse/SERVER-29326
Mongodb engineers confirmed that's a bug. Unfortunately it's not mentioned in mongodb's documentation which would have saved us many hours tracing down the issue and deploy another schema design from the beginning.

Try this index:
If your existing index db.offer.createIndex({shopId:1, missingSince:1, merchantId:1, _id:1}, {background:true})
is used for another purpose, try creating following index on offer collection:
db.offer.createIndex({missingSince:1,shopId:1}, {background:true})
This will optimize the query itself and thus count.

Related

Mongo 3.2 Sub-Document Index issue

Recently we have upgraded our MongoDB 2.6(MMAPV1) to 3.2(MMAPV1), After upgrade indexes in sub-document is not working. I did a small proof of concept on both databases with query explain.
In the MongoDB 3.2, the subdocument index is not considered, can anybody suggest the fix for this?
This is sample mongo document,
{
"_id" : ObjectId("58bff13e4e6904293cc206b4"),
"Value" : NumberLong(158),
"OVGuid" : NumberLong(0),
"Name" : "User 08/03/2017 03.55.42.782",
"CreateDate" : ISODate("2017-03-08T11:55:42.783Z"),
"RoleLst" : [
{
"_id" : NumberLong(146),
"Name" : "Role1"
},
{
"_id" : NumberLong(108),
"Name" : "Role2"
},
{
"_id" : NumberLong(29),
"Name" : "Role3"
}
]
}
I inserted nearly 100,000 data with index enabled on "RoleLst._id"(db.User.createIndex({"RoleLst._id":1})) in both Mongo DB 2.6 and 3.2.
Then I tried query explain
db.User.find({ "RoleLst" : { "$elemMatch" : { "_id" :NumberLong(200)}}}).explain()
This is the result I got from 3.2 is
Explain for sub-document query
{
"queryPlanner" : {
"plannerVersion" : 1,
"namespace" : "SubDocmentIndexChecking.User",
"indexFilterSet" : false,
"parsedQuery" : {
"RoleLst" : {
"$elemMatch" : {
"_id" : {
"$eq" : NumberLong(200)
}
}
}
},
"winningPlan" : {
"stage" : "COLLSCAN",
"filter" : {
"RoleLst" : {
"$elemMatch" : {
"_id" : {
"$eq" : NumberLong(200)
}
}
}
},
"direction" : "forward"
},
"rejectedPlans" : []
},
"serverInfo" : {
"host" : "******",
"port" : ******,
"version" : "3.2.10",
"gitVersion" : "79d9b3ab5ce20f51c272b4411202710a082d0317"
},
"ok" : 1.0
}
This is the result I got from 2.6
Explain for sub-document query
{
"cursor" : "BtreeCursor RoleLst._id_1",
"isMultiKey" : true,
"n" : 0,
"nscannedObjects" : 0,
"nscanned" : 0,
"nscannedObjectsAllPlans" : 0,
"nscannedAllPlans" : 0,
"scanAndOrder" : false,
"indexOnly" : false,
"nYields" : 0,
"nChunkSkips" : 0,
"millis" : 0,
"indexBounds" : {
"RoleLst._id" : [
[
NumberLong(200),
NumberLong(200)
]
]
},
"server" : "***********",
"filterSet" : false,
"stats" : {
"type" : "KEEP_MUTATIONS",
"works" : 2,
"yields" : 0,
"unyields" : 0,
"invalidates" : 0,
"advanced" : 0,
"needTime" : 1,
"needFetch" : 0,
"isEOF" : 1,
"children" : [
{
"type" : "FETCH",
"works" : 2,
"yields" : 0,
"unyields" : 0,
"invalidates" : 0,
"advanced" : 0,
"needTime" : 1,
"needFetch" : 0,
"isEOF" : 1,
"alreadyHasObj" : 0,
"forcedFetches" : 0,
"matchTested" : 0,
"children" : [
{
"type" : "IXSCAN",
"works" : 1,
"yields" : 0,
"unyields" : 0,
"invalidates" : 0,
"advanced" : 0,
"needTime" : 1,
"needFetch" : 0,
"isEOF" : 1,
"keyPattern" : "{ RoleLst._id: 1.0 }",
"isMultiKey" : 1,
"boundsVerbose" : "field #0['RoleLst._id']: [200, 200]",
"yieldMovedCursor" : 0,
"dupsTested" : 0,
"dupsDropped" : 0,
"seenInvalidated" : 0,
"matchTested" : 0,
"keysExamined" : 0,
"children" : []
}
]
}
]
}
}

MongoDB: What index should I use?

I got a highscore mongodb table that contains documents such as
{username:"Bob",score:10,category:"mostLikes"}
{username:"John",score:32,category:"mostLikes"}
{username:"Bob",score:2,category:"leastDeaths"}
The goal is to fetch the top 100 (sorted) of a specific category.
Important: Certain highscore categories are ascending (lower is better ex: leastDeaths) and others are descending (bigger is better ex: mostLikes). This means that depending on the category, I want either the 100 biggest scores or the 100 lowest scores.
There are two main queries in my application:
db.highscore.find({category:category}, {}).limit(100).sort({ score: 1 /*or -1*/ });
db.highscore.find({username:username});
What index would you recommend?
Would keeping ascending category and descending categories in different tables result in better performance?
Note: I do not want to have one table per category.
I did some test on my local with some sample datasets and i think the best option would be to create an index on "category_1_score_1_username_1"
Creating an index on the following fields gives you a covered query and thus the documents are returned from the index directly.
Find below my analysis
> db.usr.find();
{ "_id" : ObjectId("57bd20630744bd376277a795"), "username" : "Bob", "score" : 10, "category" : "mostLikes" }
{ "_id" : ObjectId("57bd20630744bd376277a796"), "username" : "John", "score" : 32, "category" : "mostLikes" }
{ "_id" : ObjectId("57bd20630744bd376277a797"), "username" : "Bob1", "score" : 2, "category" : "leastDeaths" }
{ "_id" : ObjectId("57bd20630744bd376277a798"), "username" : "John2", "score" : 132, "category" : "mostLikes" }
{ "_id" : ObjectId("57bd20630744bd376277a799"), "username" : "Bob3", "score" : 20, "category" : "leastDeaths" }
{ "_id" : ObjectId("57bd20630744bd376277a79a"), "username" : "John4", "score" : 132, "category" : "mostLikes" }
{ "_id" : ObjectId("57bd20630744bd376277a79b"), "username" : "Bob5", "score" : 22, "category" : "leastDeaths" }
{ "_id" : ObjectId("57bd20630744bd376277a79c"), "username" : "John6", "score" : 322, "category" : "mostLikes" }
{ "_id" : ObjectId("57bd20630744bd376277a79d"), "username" : "Bob7", "score" : 232, "category" : "leastDeaths" }
{ "_id" : ObjectId("57bd20630744bd376277a79e"), "username" : "John8", "score" : 3112, "category" : "mostLikes" }
{ "_id" : ObjectId("57bd20630744bd376277a79f"), "username" : "Bob4", "score" : 222, "category" : "leastDeaths" }
{ "_id" : ObjectId("57bd20630744bd376277a7a0"), "username" : "John22", "score" : 3210, "category" : "mostLikes" }
{ "_id" : ObjectId("57bd20630744bd376277a7a1"), "username" : "Bob33", "score" : 2111, "category" : "leastDeaths" }
Indexes:
> db.usr.getIndexes();
{
"v" : 1,
"key" : {
"category" : 1,
"score" : 1,
"username" : 1
},
"name" : "category_1_score_1_username_1",
"ns" : "test.usr"
}
]
>
Now you can modify your query slightly to make it return a covered query.
db.usr.find({"category":"mostLikes"},{"_id":0,"score":-1,"category":1,"username":1}).sort({"score":1}).explain("executionStats");
Output of Execution Stats:
{
"queryPlanner" : {
"plannerVersion" : 1,
"namespace" : "test.usr",
"indexFilterSet" : false,
"parsedQuery" : {
"category" : {
"$eq" : "mostLikes"
}
},
"winningPlan" : {
"stage" : "PROJECTION",
"transformBy" : {
"_id" : 0,
"score" : -1,
"category" : 1,
"username" : 1
},
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"category" : 1,
"score" : 1,
"username" : 1
},
"indexName" : "category_1_score_1_username_1",
"isMultiKey" : false,
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 1,
"direction" : "forward",
"indexBounds" : {
"category" : [
"[\"mostLikes\", \"mostLikes\"]"
],
"score" : [
"[MinKey, MaxKey]"
],
"username" : [
"[MinKey, MaxKey]"
]
}
}
},
"rejectedPlans" : [ ]
},
"executionStats" : {
"executionSuccess" : true,
"nReturned" : 7,
"executionTimeMillis" : 0,
"totalKeysExamined" : 7,
"totalDocsExamined" : 0,
"executionStages" : {
"stage" : "PROJECTION",
"nReturned" : 7,
"executionTimeMillisEstimate" : 0,
"works" : 8,
"advanced" : 7,
"needTime" : 0,
"needYield" : 0,
"saveState" : 0,
"restoreState" : 0,
"isEOF" : 1,
"invalidates" : 0,
"transformBy" : {
"_id" : 0,
"score" : -1,
"category" : 1,
"username" : 1
},
"inputStage" : {
"stage" : "IXSCAN",
"nReturned" : 7,
"executionTimeMillisEstimate" : 0,
"works" : 8,
"advanced" : 7,
"needTime" : 0,
"needYield" : 0,
"saveState" : 0,
"restoreState" : 0,
"isEOF" : 1,
"invalidates" : 0,
"keyPattern" : {
"category" : 1,
"score" : 1,
"username" : 1
},
"indexName" : "category_1_score_1_username_1",
"isMultiKey" : false,
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 1,
"direction" : "forward",
"indexBounds" : {
"category" : [
"[\"mostLikes\", \"mostLikes\"]"
],
"score" : [
"[MinKey, MaxKey]"
],
"username" : [
"[MinKey, MaxKey]"
]
},
"keysExamined" : 7,
"dupsTested" : 0,
"dupsDropped" : 0,
"seenInvalidated" : 0
}
}
},
"serverInfo" : {
"host" : "L4156409",
"port" : 27017,
"version" : "3.2.5",
"gitVersion" : "34e65e5383f7ea1726332cb175b73077ec4a1b02"
},
"ok" : 1
}
>
Thus as you can see the output the no of documents scanned is 0 while the records are fetched directly from the index. Thus choosing this index would be your best bet for the first query.
For the second query, that should be simple to create an index on the username field and that should solve the second query for you.
HTH.

MongoDB efficient way to search for objectId field with null using Index?

I am trying to find an efficient way to search for items in which specific field is null.
In the MongoDB, I have folder schema which has parent field of its parent folder's ObjectId and parent is indexed. For root folders, parent fields are null.
When I try to find all the root folders with parent:null, explain displays indexOnly: false
db.folders.find({parent: null}, {parent: 1, _id: 0}).explain()
{
"cursor" : "BtreeCursor parent_1",
"isMultiKey" : false,
"n" : 126,
"nscannedObjects" : 126,
"nscanned" : 126,
"nscannedObjectsAllPlans" : 126,
"nscannedAllPlans" : 126,
"scanAndOrder" : false,
"indexOnly" : false,
"nYields" : 1,
"nChunkSkips" : 0,
"millis" : 0,
"indexBounds" : {
"parent" : [
[
null,
null
]
]
},
"server" : "c268.candidate.36:10268",
"filterSet" : false,
"stats" : {
"type" : "PROJECTION",
"works" : 128,
"yields" : 1,
"unyields" : 1,
"invalidates" : 0,
"advanced" : 126,
"needTime" : 0,
"needFetch" : 0,
"isEOF" : 1,
"children" : [
{
"type" : "KEEP_MUTATIONS",
"works" : 128,
"yields" : 1,
"unyields" : 1,
"invalidates" : 0,
"advanced" : 126,
"needTime" : 1,
"needFetch" : 0,
"isEOF" : 1,
"children" : [
{
"type" : "FETCH",
"works" : 128,
"yields" : 1,
"unyields" : 1,
"invalidates" : 0,
"advanced" : 126,
"needTime" : 1,
"needFetch" : 0,
"isEOF" : 1,
"alreadyHasObj" : 0,
"forcedFetches" : 0,
"matchTested" : 126,
"children" : [
{
"type" : "IXSCAN",
"works" : 127,
"yields" : 1,
"unyields" : 1,
"invalidates" : 0,
"advanced" : 126,
"needTime" : 1,
"needFetch" : 0,
"isEOF" : 1,
"keyPattern" : "{ parent: 1.0 }",
"isMultiKey" : 0,
"boundsVerbose" : "field #0['parent']: [null, null]",
"yieldMovedCursor" : 0,
"dupsTested" : 0,
"dupsDropped" : 0,
"seenInvalidated" : 0,
"matchTested" : 0,
"keysExamined" : 126,
"children" : []
}
]
}
]
}
]
}
}
I tried to use $type to find any ones which parent folder is not ObjectId. But still indexOnly:false.
db.folders.find({parent: {$ne: {$type: 7}}}, {parent: 1, _id: 0}).explain()
"indexOnly": false
Is there a way to search null value only using index? If not, is there a better value to store instead of null to be able to search with index?
Additional
example of root folder
{
"_id" : ObjectId("55a04a2d754971030059b7ad"),
"active" : true,
"modified" : ISODate("2016-02-05T22:30:08.053Z"),
"created" : ISODate("2015-07-10T22:41:49.009Z"),
"user" : ObjectId("54d3ae187a738c0300f59e61"),
"name" : "2nd Grade",
"parent" : null,
"clientModified" : ISODate("2016-02-05T22:30:07.872Z"),
"userCreated" : ISODate("2015-07-10T22:41:48.328Z"),
"ancestors" : [],
"__v" : 2
}
example of child folder
{
"_id" : ObjectId("56d0b4edb6f05e03009bcabc"),
"active" : true,
"modified" : ISODate("2016-02-26T20:26:21.328Z"),
"created" : ISODate("2016-02-26T20:26:21.328Z"),
"user" : ObjectId("54d3ae187a738c0300f59e61"),
"name" : "music",
"parent" : ObjectId("55a04a2d754971030059b7ad"),
"clientModified" : ISODate("2016-02-26T20:26:20.398Z"),
"userCreated" : ISODate("2016-02-26T20:26:20.398Z"),
"ancestors" : [
ObjectId("55a04a2d754971030059b7ad")
],
"__v" : 0
}
db.folders.getIndexes()
{
"0" : {
"v" : 1,
"key" : {
"_id" : 1
},
"name" : "_id_",
"ns" : "app29099188.folders"
},
"1" : {
"v" : 1,
"key" : {
"user" : 1,
"_fts" : "text",
"_ftsx" : 1
},
"name" : "user_1_name_text",
"ns" : "app29099188.folders",
"background" : true,
"safe" : null,
"weights" : {
"name" : 1
},
"default_language" : "english",
"language_override" : "language",
"textIndexVersion" : 2
},
"2" : {
"v" : 1,
"key" : {
"user" : 1,
"parent" : 1
},
"name" : "user_1_parent_1",
"ns" : "app29099188.folders",
"background" : true,
"safe" : null
},
"3" : {
"v" : 1,
"key" : {
"parent" : 1.0000000000000000
},
"name" : "parent_1",
"ns" : "app29099188.folders"
}
}
After comment - update:
The way to eliminate docScan is to have a value in parent field. It can be zeroed objectId or just "/" as a root.
db.satoko.insert({"test":"sdsf", parent: "/"})
db.satoko.insert({"test":"sds33f", parent: "/"})
db.satoko.insert({"parent":ObjectId("56d8b2879bd059e7247a6096"), "test":"sdsf"})
explain results:
db.satoko.find({parent:{$eq:"/"} }, {parent: 1, _id: 0}).explain("allPlansExec
ution")
{
"queryPlanner" : {
"plannerVersion" : 1,
"namespace" : "test.satoko",
"indexFilterSet" : false,
"parsedQuery" : {
"parent" : {
"$eq" : "/"
}
},
"winningPlan" : {
"stage" : "PROJECTION",
"transformBy" : {
"parent" : 1,
"_id" : 0
},
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"parent" : 1
},
"indexName" : "parent_1",
"isMultiKey" : false,
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 1,
"direction" : "forward",
"indexBounds" : {
"parent" : [
"[\"/\", \"/\"]"
]
}
}
},
"rejectedPlans" : [ ]
},
"executionStats" : {
"executionSuccess" : true,
"nReturned" : 2,
"executionTimeMillis" : 0,
"totalKeysExamined" : 2,
"totalDocsExamined" : 0,
"executionStages" : {
"stage" : "PROJECTION",
"nReturned" : 2,
"executionTimeMillisEstimate" : 0,
"works" : 3,
"advanced" : 2,
"needTime" : 0,
"needYield" : 0,
"saveState" : 0,
"restoreState" : 0,
"isEOF" : 1,
"invalidates" : 0,
"transformBy" : {
"parent" : 1,
"_id" : 0
},
"inputStage" : {
"stage" : "IXSCAN",
"nReturned" : 2,
"executionTimeMillisEstimate" : 0,
"works" : 3,
"advanced" : 2,
"needTime" : 0,
"needYield" : 0,
"saveState" : 0,
"restoreState" : 0,
"isEOF" : 1,
"invalidates" : 0,
"keyPattern" : {
"parent" : 1
},
"indexName" : "parent_1",
"isMultiKey" : false,
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 1,
"direction" : "forward",
"indexBounds" : {
"parent" : [
"[\"/\", \"/\"]"
]
},
"keysExamined" : 2,
"dupsTested" : 0,
"dupsDropped" : 0,
"seenInvalidated" : 0
}
},
"allPlansExecution" : [ ]
},
"serverInfo" : {
"host" : "greg",
"port" : 27017,
"version" : "3.2.3",
"gitVersion" : "b326ba837cf6f49d65c2f85e1b70f6f31ece7937"
},
"ok" : 1
}

Mongodb $and and $sort on different fields and getting last matching record

I have a collection with around 50 lake records.Below is one sample document
{
"_id" : NumberLong(4253223),
"locId" : 59,
"startIpNum" : NumberLong("3287940726"),
"endIpNum" : NumberLong("3287940761"),
"maxmind_location" : {
"locId" : 59,
"country" : "DK",
"region" : "",
"city" : "",
"postalCode" : "",
"latitude" : "56.0000",
"longitude" : "10.0000",
"metroCode" : "",
"areaCode" : "\n"
}
}
Below is Query I am trying to perform.I want to find last record from the matching condition.
find({
$and: [
{startIpNum: { $lte: 459950297 }},
{endIpNum: { $gte: 459950297 }}
]
}).sort({_id : -1}).limit(1)
I have septate ascending index on startIpNum and endIpNum. I have replaced _id with incremental id value like Mysql.
When I do query without sort and with limit 1. It gives me result in 0ms. As soon as I put sort (I need sort since I want last matching record ) the query get hang forever.
I have also tried below query, but It takes around 700ms. With compound
index on {startIpNum :1 , endIpNum : 1 , _id : -1 } with the sort on _id.
find({
startIpNum : { $lte: 459950297 },
endIpNum : { $gte: 459950297 }
}).sort({
startIpNum :1,
endIpNum :1 ,
_id : -1
}).limit(1).explain({ verbose : true});
How can I achieve sort in my first approach.
Here is explain. Its still scanning 370061 indexes for
db.maxmind.find({startIpNum : { $lte: 459950297 }, endIpNum : { $gte: 459950297 } }).sort({startIpNum :1, endIpNum :1 , _id : -1 }).limit(1).hint("startIpNum_1_endIpNum_1__id_-1").explain( { verbose: true } );
{
"queryPlanner" : {
"plannerVersion" : 1,
"namespace" : "yogeshTest.maxmind",
"indexFilterSet" : false,
"parsedQuery" : {
"$and" : [
{
"startIpNum" : {
"$lte" : 459950297
}
},
{
"endIpNum" : {
"$gte" : 459950297
}
}
]
},
"winningPlan" : {
"stage" : "LIMIT",
"limitAmount" : 0,
"inputStage" : {
"stage" : "FETCH",
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"startIpNum" : 1,
"endIpNum" : 1,
"_id" : -1
},
"indexName" : "startIpNum_1_endIpNum_1__id_-1",
"isMultiKey" : false,
"direction" : "forward",
"indexBounds" : {
"startIpNum" : [
"[-inf.0, 459950297.0]"
],
"endIpNum" : [
"[459950297.0, inf.0]"
],
"_id" : [
"[MaxKey, MinKey]"
]
}
}
}
},
"rejectedPlans" : [ ]
},
"executionStats" : {
"executionSuccess" : true,
"nReturned" : 1,
"executionTimeMillis" : 433,
"totalKeysExamined" : 370061,
"totalDocsExamined" : 1,
"executionStages" : {
"stage" : "LIMIT",
"nReturned" : 1,
"executionTimeMillisEstimate" : 430,
"works" : 370062,
"advanced" : 1,
"needTime" : 370060,
"needFetch" : 0,
"saveState" : 2891,
"restoreState" : 2891,
"isEOF" : 1,
"invalidates" : 0,
"limitAmount" : 0,
"inputStage" : {
"stage" : "FETCH",
"nReturned" : 1,
"executionTimeMillisEstimate" : 420,
"works" : 370061,
"advanced" : 1,
"needTime" : 370060,
"needFetch" : 0,
"saveState" : 2891,
"restoreState" : 2891,
"isEOF" : 0,
"invalidates" : 0,
"docsExamined" : 1,
"alreadyHasObj" : 0,
"inputStage" : {
"stage" : "IXSCAN",
"nReturned" : 1,
"executionTimeMillisEstimate" : 410,
"works" : 370061,
"advanced" : 1,
"needTime" : 370060,
"needFetch" : 0,
"saveState" : 2891,
"restoreState" : 2891,
"isEOF" : 0,
"invalidates" : 0,
"keyPattern" : {
"startIpNum" : 1,
"endIpNum" : 1,
"_id" : -1
},
"indexName" : "startIpNum_1_endIpNum_1__id_-1",
"isMultiKey" : false,
"direction" : "forward",
"indexBounds" : {
"startIpNum" : [
"[-inf.0, 459950297.0]"
],
"endIpNum" : [
"[459950297.0, inf.0]"
],
"_id" : [
"[MaxKey, MinKey]"
]
},
"keysExamined" : 370061,
"dupsTested" : 0,
"dupsDropped" : 0,
"seenInvalidated" : 0,
"matchTested" : 0
}
}
},
"allPlansExecution" : [ ]
},
"serverInfo" : {
"host" : "cus360-H81M-S",
"port" : 27017,
"version" : "3.0.3",
"gitVersion" : "b40106b36eecd1b4407eb1ad1af6bc60593c6105"
},
"ok" : 1
}
Before you post the output for db.collection.getIndexes() and explain of your query, let us try the following. What I suspect is that your {startIpNum :1 , endIpNum : 1 , _id : -1 } does not win as a query plan.
So what you can try is force MongoDB to use that index by hinting:
find({
startIpNum : { $lte: 459950297 },
endIpNum : { $gte: 459950297 }
}).sort({
startIpNum :1,
endIpNum :1 ,
_id : -1
}).limit(1).hint({startIpNum :1 , endIpNum : 1 , _id : -1 })
Currently it seems like your query fetches all the matching documents, loads them into memory, and sorts them there. With hinting, using your index, it will just pick your documents in the right order initially.

mongodb index find index sort

I would like to ask about mongodb indexes. Can I use a different index in the find and the sort. By example I have two indexes:
(a:-1)
(b:1,c:1)
What indexes uses this sentence?
({a:[$gt30}},{a:[$lt50}}]}.sort({c:1})
Can I use a different index in the find and the sort.
After reading some more into this you will see at the bottom of the documentation page on index intersectioning: http://docs.mongodb.org/manual/core/index-intersection/#index-intersection-and-sort
Index intersection does not apply when the sort() operation requires an index completely separate from the query predicate.
So no, even if ypou created an index of {c:1} it could not be used independantly to intersect {a:1}
What indexes uses this sentence?
In this case only {a:1} will be used.
Creating an index on a single field called Single Field Index.
Creating multiple Single Field indexes to boost your query and the sort performance won't help much!. You should use Compound Indexes instead.
Check the documentation on MongoDB: https://docs.mongodb.com/manual/core/index-compound/
If you want to learn how to index your fields and how to measure the performance of your queries.
And Check this tutorial on Youtube: https://dplink.app/nxLgvk7lR
Suppose I am having persons collection having documents like below :
{
dob:
{ age : 50} ,
gender : "male" ,
phone : ""
}
Now i create indexes as below .
1 : db.persons.createIndex({"dob.age" : -1})
2 : db.persons.createIndex({phone : 1 , gender : 1})
Now If i execute below query like yours
db.persons.explain("executionStats").find({$and : [ {"dob.age" : {$lt : 50} } ,
{"dob.age" : {$gt : 30} } ] } ).sort({gender : 1 })
I will get below execution stats :
{
"queryPlanner" : {
"plannerVersion" : 1,
"namespace" : "college.persons",
"indexFilterSet" : false,
"parsedQuery" : {
"$and" : [
{
"dob.age" : {
"$lt" : 50
}
},
{
"dob.age" : {
"$gt" : 30
}
}
]
},
"queryHash" : "22FEA299",
"planCacheKey" : "5E8F38C1",
"winningPlan" : {
"stage" : "SORT",
"sortPattern" : {
"phone" : 1
},
"inputStage" : {
"stage" : "SORT_KEY_GENERATOR",
"inputStage" : {
"stage" : "FETCH",
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"dob.age" : -1
},
"indexName" : "dob.age_-1",
"isMultiKey" : false,
"multiKeyPaths" : {
"dob.age" : [ ]
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "forward",
"indexBounds" : {
"dob.age" : [
"(50.0, 30.0)"
]
}
}
}
}
},
"rejectedPlans" : [ ]
},
"executionStats" : {
"executionSuccess" : true,
"nReturned" : 1734,
"executionTimeMillis" : 10,
"totalKeysExamined" : 1734,
"totalDocsExamined" : 1734,
"executionStages" : {
"stage" : "SORT",
"nReturned" : 1734,
"executionTimeMillisEstimate" : 0,
"works" : 3471,
"advanced" : 1734,
"needTime" : 1736,
"needYield" : 0,
"saveState" : 27,
"restoreState" : 27,
"isEOF" : 1,
"sortPattern" : {
"phone" : 1
},
"memUsage" : 1914799,
"memLimit" : 33554432,
"inputStage" : {
"stage" : "SORT_KEY_GENERATOR",
"nReturned" : 1734,
"executionTimeMillisEstimate" : 0,
"works" : 1736,
"advanced" : 1734,
"needTime" : 1,
"needYield" : 0,
"saveState" : 27,
"restoreState" : 27,
"isEOF" : 1,
"inputStage" : {
"stage" : "FETCH",
"nReturned" : 1734,
"executionTimeMillisEstimate" : 0,
"works" : 1735,
"advanced" : 1734,
"needTime" : 0,
"needYield" : 0,
"saveState" : 27,
"restoreState" : 27,
"isEOF" : 1,
"docsExamined" : 1734,
"alreadyHasObj" : 0,
"inputStage" : {
"stage" : "IXSCAN",
"nReturned" : 1734,
"executionTimeMillisEstimate" : 0,
"works" : 1735,
"advanced" : 1734,
"needTime" : 0,
"needYield" : 0,
"saveState" : 27,
"restoreState" : 27,
"isEOF" : 1,
"keyPattern" : {
"dob.age" : -1
},
"indexName" : "dob.age_-1",
"isMultiKey" : false,
"multiKeyPaths" : {
"dob.age" : [ ]
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "forward",
"indexBounds" : {
"dob.age" : [
"(50.0, 30.0)"
]
},
"keysExamined" : 1734,
"seeks" : 1,
"dupsTested" : 0,
"dupsDropped" : 0
}
}
}
}
},
"serverInfo" : {
"host" : "RGGYSLT-0483",
"port" : 27017,
"version" : "4.2.0",
"gitVersion" : "a4b751dcf51dd249c5865812b390cfd1c0129c30"
},
"ok" : 1
}
This Means Data was fetched with IXScan on single field first and then sorted on
third field separately .
But the moment i change the query to sort on fields for which the index is already created things change . Now {"dob.age" : -1} index gets rejected .
In Mongo db Winning plan is the one for which 100 docs can be fetched early and Mongo db caches that plan for a query . Now this cache will be purged after 1000 docs insertions , index rebuild , server restarts or new index insertions.
Hence which index will be used depends upon winning plans .
{
"queryPlanner" : {
"plannerVersion" : 1,
"namespace" : "college.persons",
"indexFilterSet" : false,
"parsedQuery" : {
"$and" : [
{
"dob.age" : {
"$lt" : 50
}
},
{
"dob.age" : {
"$gt" : 30
}
}
]
},
"queryHash" : "DA8248FA",
"planCacheKey" : "E779554F",
"winningPlan" : {
"stage" : "FETCH",
"filter" : {
"$and" : [
{
"dob.age" : {
"$lt" : 50
}
},
{
"dob.age" : {
"$gt" : 30
}
}
]
},
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"gender" : 1,
"phone" : 1
},
"indexName" : "gender_1_phone_1",
"isMultiKey" : false,
"multiKeyPaths" : {
"gender" : [ ],
"phone" : [ ]
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "forward",
"indexBounds" : {
"gender" : [
"[MinKey, MaxKey]"
],
"phone" : [
"[MinKey, MaxKey]"
]
}
}
},
"rejectedPlans" : [
{
"stage" : "SORT",
"sortPattern" : {
"gender" : 1,
"phone" : 1
},
"inputStage" : {
"stage" : "SORT_KEY_GENERATOR",
"inputStage" : {
"stage" : "FETCH",
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"dob.age" : -1
},
"indexName" : "dob.age_-1",
"isMultiKey" : false,
"multiKeyPaths" : {
"dob.age" : [ ]
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "forward",
"indexBounds" : {
"dob.age" : [
"(50.0, 30.0)"
]
}
}
}
}
}
]
},
"executionStats" : {
"executionSuccess" : true,
"nReturned" : 1734,
"executionTimeMillis" : 12,
"totalKeysExamined" : 5002,
"totalDocsExamined" : 5002,
"executionStages" : {
"stage" : "FETCH",
"filter" : {
"$and" : [
{
"dob.age" : {
"$lt" : 50
}
},
{
"dob.age" : {
"$gt" : 30
}
}
]
},
"nReturned" : 1734,
"executionTimeMillisEstimate" : 0,
"works" : 5003,
"advanced" : 1734,
"needTime" : 3268,
"needYield" : 0,
"saveState" : 41,
"restoreState" : 41,
"isEOF" : 1,
"docsExamined" : 5002,
"alreadyHasObj" : 0,
"inputStage" : {
"stage" : "IXSCAN",
"nReturned" : 5002,
"executionTimeMillisEstimate" : 0,
"works" : 5003,
"advanced" : 5002,
"needTime" : 0,
"needYield" : 0,
"saveState" : 41,
"restoreState" : 41,
"isEOF" : 1,
"keyPattern" : {
"gender" : 1,
"phone" : 1
},
"indexName" : "gender_1_phone_1",
"isMultiKey" : false,
"multiKeyPaths" : {
"gender" : [ ],
"phone" : [ ]
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "forward",
"indexBounds" : {
"gender" : [
"[MinKey, MaxKey]"
],
"phone" : [
"[MinKey, MaxKey]"
]
},
"keysExamined" : 5002,
"seeks" : 1,
"dupsTested" : 0,
"dupsDropped" : 0
}
}
},
"serverInfo" : {
"host" : "RGGYSLT-0483",
"port" : 27017,
"version" : "4.2.0",
"gitVersion" : "a4b751dcf51dd249c5865812b390cfd1c0129c30"
},
"ok" : 1
}