Understanding explain() to optimize complex queries - mongodb

I have a aggregate pipeline that takes over 5 seconds to return 200 rows.
I'm trying to optimize my current pipeline using explain("executionStats").
This is my pipeline:
db.getCollection("content_topics").explain("executionStats").aggregate([{
"$lookup": {
from: "users",
localField: "creator",
foreignField: "_id",
as: "user"
}
},
{
$unwind: "$user"
},
{
"$match": {
created_at: {
"$gte": 1528914600,
"$lte": 1534271400
},
dash_status: 3,
language: "hi",
parent_topic_id: {
"$eq": null
},
status: 1,
"user.device_os": {
"$ne": "BOT"
}
}
},
{
"$sort": {
created_at: -1
}
},
{
"$addFields": {
user_handle: "$user.handle",
user_phone: "$user.phone",
user_status: "$user.status"
}
},
{
"$project": {
topic_id: 1,
n_vokes: 1,
message: 1,
title: 1,
language: 1,
description: 1,
voice_desc: 1,
image: 1,
image_share: 1,
hashtag: 1,
location: 1,
default_text: 1,
creator: 1,
created_at: 1,
status: 1,
ref_id: 1,
weightage: 1,
username: 1,
slug_generated: 1,
user_handle: 1,
user_phone: 1,
user_status: 1
}
},
{
"$skip": 0
},
{
"$limit": 200
}
]);
And this is what mongo explain()ed:
{
"stages" : [
{
"$cursor" : {
"query" : {
"$and" : [
{
"created_at" : {
"$gte" : 1528914600.0
}
},
{
"created_at" : {
"$lte" : 1534271400.0
}
},
{
"dash_status" : {
"$eq" : 3.0
}
},
{
"language" : {
"$eq" : "hi"
}
},
{
"parent_topic_id" : {
"$eq" : null
}
},
{
"status" : {
"$eq" : 1.0
}
}
]
},
"fields" : {
"created_at" : 1.0,
"creator" : 1.0,
"default_text" : 1.0,
"description" : 1.0,
"hashtag" : 1.0,
"image" : 1.0,
"image_share" : 1.0,
"language" : 1.0,
"location" : 1.0,
"message" : 1.0,
"n_vokes" : 1.0,
"ref_id" : 1.0,
"slug_generated" : 1.0,
"status" : 1.0,
"title" : 1.0,
"topic_id" : 1.0,
"user.handle" : 1.0,
"user.phone" : 1.0,
"user.status" : 1.0,
"user_handle" : 1.0,
"user_phone" : 1.0,
"user_status" : 1.0,
"username" : 1.0,
"voice_desc" : 1.0,
"weightage" : 1.0,
"_id" : 1.0
},
"queryPlanner" : {
"plannerVersion" : 1.0,
"namespace" : "vokalapp.content_topics",
"indexFilterSet" : false,
"parsedQuery" : {
"$and" : [
{
"dash_status" : {
"$eq" : 3.0
}
},
{
"language" : {
"$eq" : "hi"
}
},
{
"parent_topic_id" : {
"$eq" : null
}
},
{
"status" : {
"$eq" : 1.0
}
},
{
"created_at" : {
"$lte" : 1534271400.0
}
},
{
"created_at" : {
"$gte" : 1528914600.0
}
}
]
},
"winningPlan" : {
"stage" : "FETCH",
"filter" : {
"parent_topic_id" : {
"$eq" : null
}
},
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"dash_status" : 1.0,
"status" : 1.0,
"language" : 1.0,
"parent_topic_id" : 1.0,
"created_at" : 1.0
},
"indexName" : "index_for_dashboard",
"isMultiKey" : false,
"multiKeyPaths" : {
"dash_status" : [
],
"status" : [
],
"language" : [
],
"parent_topic_id" : [
],
"created_at" : [
]
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2.0,
"direction" : "forward",
"indexBounds" : {
"dash_status" : [
"[3.0, 3.0]"
],
"status" : [
"[1.0, 1.0]"
],
"language" : [
"[\"hi\", \"hi\"]"
],
"parent_topic_id" : [
"[null, null]"
],
"created_at" : [
"[1528914600.0, 1534271400.0]"
]
}
}
},
"rejectedPlans" : [
{
"stage" : "FETCH",
"filter" : {
"$and" : [
{
"dash_status" : {
"$eq" : 3.0
}
},
{
"language" : {
"$eq" : "hi"
}
},
{
"parent_topic_id" : {
"$eq" : null
}
},
{
"status" : {
"$eq" : 1.0
}
}
]
},
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"created_at" : 1.0
},
"indexName" : "created_at_index",
"isMultiKey" : false,
"multiKeyPaths" : {
"created_at" : [
]
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2.0,
"direction" : "forward",
"indexBounds" : {
"created_at" : [
"[1528914600.0, 1534271400.0]"
]
}
}
},
{
"stage" : "FETCH",
"filter" : {
"$and" : [
{
"dash_status" : {
"$eq" : 3.0
}
},
{
"parent_topic_id" : {
"$eq" : null
}
}
]
},
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"language" : 1.0,
"status" : 1.0,
"created_at" : 1.0,
"answers.type" : 1.0
},
"indexName" : "language_status_created_at_answer_type_index",
"isMultiKey" : true,
"multiKeyPaths" : {
"language" : [
],
"status" : [
],
"created_at" : [
],
"answers.type" : [
"answers"
]
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2.0,
"direction" : "forward",
"indexBounds" : {
"language" : [
"[\"hi\", \"hi\"]"
],
"status" : [
"[1.0, 1.0]"
],
"created_at" : [
"[1528914600.0, 1534271400.0]"
],
"answers.type" : [
"[MinKey, MaxKey]"
]
}
}
},
{
"stage" : "FETCH",
"filter" : {
"$and" : [
{
"dash_status" : {
"$eq" : 3.0
}
},
{
"parent_topic_id" : {
"$eq" : null
}
}
]
},
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"language" : -1.0,
"status" : -1.0,
"weightage" : -1.0,
"created_at" : -1.0
},
"indexName" : "language_status_weightage_created_at_index",
"isMultiKey" : false,
"multiKeyPaths" : {
"language" : [
],
"status" : [
],
"weightage" : [
],
"created_at" : [
]
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2.0,
"direction" : "forward",
"indexBounds" : {
"language" : [
"[\"hi\", \"hi\"]"
],
"status" : [
"[1.0, 1.0]"
],
"weightage" : [
"[MaxKey, MinKey]"
],
"created_at" : [
"[1534271400.0, 1528914600.0]"
]
}
}
}
]
},
"executionStats" : {
"executionSuccess" : true,
"nReturned" : 7072.0,
"executionTimeMillis" : 984.0,
"totalKeysExamined" : 7072.0,
"totalDocsExamined" : 7072.0,
"executionStages" : {
"stage" : "FETCH",
"filter" : {
"parent_topic_id" : {
"$eq" : null
}
},
"nReturned" : 7072.0,
"executionTimeMillisEstimate" : 40.0,
"works" : 7073.0,
"advanced" : 7072.0,
"needTime" : 0.0,
"needYield" : 0.0,
"saveState" : 63.0,
"restoreState" : 63.0,
"isEOF" : 1.0,
"invalidates" : 0.0,
"docsExamined" : 7072.0,
"alreadyHasObj" : 0.0,
"inputStage" : {
"stage" : "IXSCAN",
"nReturned" : 7072.0,
"executionTimeMillisEstimate" : 0.0,
"works" : 7073.0,
"advanced" : 7072.0,
"needTime" : 0.0,
"needYield" : 0.0,
"saveState" : 63.0,
"restoreState" : 63.0,
"isEOF" : 1.0,
"invalidates" : 0.0,
"keyPattern" : {
"dash_status" : 1.0,
"status" : 1.0,
"language" : 1.0,
"parent_topic_id" : 1.0,
"created_at" : 1.0
},
"indexName" : "index_for_dashboard",
"isMultiKey" : false,
"multiKeyPaths" : {
"dash_status" : [
],
"status" : [
],
"language" : [
],
"parent_topic_id" : [
],
"created_at" : [
]
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2.0,
"direction" : "forward",
"indexBounds" : {
"dash_status" : [
"[3.0, 3.0]"
],
"status" : [
"[1.0, 1.0]"
],
"language" : [
"[\"hi\", \"hi\"]"
],
"parent_topic_id" : [
"[null, null]"
],
"created_at" : [
"[1528914600.0, 1534271400.0]"
]
},
"keysExamined" : 7072.0,
"seeks" : 1.0,
"dupsTested" : 0.0,
"dupsDropped" : 0.0,
"seenInvalidated" : 0.0
}
}
}
}
},
{
"$lookup" : {
"from" : "users",
"as" : "user",
"localField" : "creator",
"foreignField" : "_id",
"unwinding" : {
"preserveNullAndEmptyArrays" : false
},
"matching" : {
"$nor" : [
{
"device_os" : {
"$eq" : "BOT"
}
}
]
}
}
},
{
"$sort" : {
"sortKey" : {
"created_at" : -1.0
},
"limit" : NumberLong(200)
}
},
{
"$addFields" : {
"user_handle" : "$user.handle",
"user_phone" : "$user.phone",
"user_status" : "$user.status"
}
},
{
"$project" : {
"_id" : true,
"hashtag" : true,
"voice_desc" : true,
"description" : true,
"location" : true,
"language" : true,
"topic_id" : true,
"image" : true,
"slug_generated" : true,
"n_vokes" : true,
"ref_id" : true,
"image_share" : true,
"message" : true,
"created_at" : true,
"default_text" : true,
"weightage" : true,
"user_handle" : true,
"title" : true,
"status" : true,
"creator" : true,
"user_status" : true,
"username" : true,
"user_phone" : true
}
}
],
"ok" : 1.0
}
This is after I created the index "index_for_dashboard" with the keys that I use in "$match". I also created an index for "device_os" from user collection. But to no dice, no improvements in the response time.
Possible culprits:
Is $lookup expensive. If it is can I fetch only the fields I need.
Can the indexing be done better. Should I use a different set of
fields?
Is $addField expensive. If it is I can offload it to
application level.
How should I troubleshoot(and understand) with the explain result to help myself optimize the query?
I can't make an educated call here, need some directions.

First, you want to make sure that you are running at least v3.6.3 of MongoDB because in v3.6 there's a new feature that allows $lookup to specify sub-pipelines. Those pipelines can actually use indexes, however, there was a bug which only got fixed in the version mentioned above.
The following should be as fast as things can get here:
db.getCollection("content_topics").createIndex({ created_at: -1, dash_status: 1, language: 1, parent_topic_id: 1, status: 1 }); // this index will get used by the main $match and the $sort stage
db.getCollection("users").createIndex({ device_os: 1 }); // this index will get used by the sub-pipeline in $lookup
db.getCollection("content_topics").aggregate([{
"$match": { // filter at the start in order to be able to use indexes
created_at: {
"$gte": 1528914600,
"$lte": 1534271400
},
dash_status: 3,
language: "hi",
parent_topic_id: {
"$eq": null
},
status: 1
// see the below $lookup stage in case you're wondering where the user filter went
}
},
{
"$sort": {
created_at: -1 // sort straight away so the index can be used
}
},
{
"$lookup": {
from: "users",
let: { "creator": "$creator" },
pipeline: [{ // use new v3.6 pipeline syntax to be able to leverage indexes
$match: {
$expr: {
$and: [
{ $eq: [ "$_id", "$$creator" ] },
{ $ne: [ "$device_os", "BOT" ] } // here is the "device_os" filter inside the pipeline so index can be used
]
}
}
}, {
$project: {
_id: 0, // "_id" field is not needed
user_handle: 1, // only those fields are of interest
user_phone: 1,
user_status: 1
}
}],
as: "user"
}
},
{
$unwind: "$user"
},
{
"$project": {
topic_id: 1,
n_vokes: 1,
message: 1,
title: 1,
language: 1,
description: 1,
voice_desc: 1,
image: 1,
image_share: 1,
hashtag: 1,
location: 1,
default_text: 1,
creator: 1,
created_at: 1,
status: 1,
ref_id: 1,
weightage: 1,
username: 1,
slug_generated: 1,
user_handle: "$user.handle", // no extra $addFields stage needed
user_phone: "$user.phone", // same here
user_status: "$user.status" // and here
}
},
{
"$skip": 0
},
{
"$limit": 200
}
]);

Related

MongoDB 4.4 slow query

I execute a query command in mongos, but it executes for a long time.
mongos> db.message.find({"toid":123,"$or":[{"rid":2},{"validtime":null},{"fromid":2},{"validtime":{"$lt":1649565335}}]}).sort({sortid:-1}).limit(10)
slow query log like this:
{
"op" : "command",
"ns" : "sns.message",
"command" : {
"explain" : {
"find" : "message",
"filter" : {
"toid" : 123,
"$or" : [
{
"rid" : 2
},
{
"validtime" : null
},
{
"fromid" : 2
},
{
"validtime" : {
"$lt" : 1649565335
}
}
]
},
"limit" : 10,
"singleBatch" : true,
"sort" : {
"sortid" : -1
},
"options" : {
}
},
"verbosity" : "executionStats",
"$db" : "sns"
},
"numYield" : 6477,
"locks" : {
"ReplicationStateTransition" : {
"acquireCount" : {
"w" : NumberLong(6479)
}
},
"Global" : {
"acquireCount" : {
"r" : NumberLong(6479)
}
},
"Database" : {
"acquireCount" : {
"r" : NumberLong(6478)
}
},
"Collection" : {
"acquireCount" : {
"r" : NumberLong(6478)
}
},
"Mutex" : {
"acquireCount" : {
"r" : NumberLong(1)
}
}
},
"flowControl" : {
},
"storage" : {
"data" : {
"bytesRead" : NumberLong(91629495),
"timeReadingMicros" : NumberLong(68943)
}
},
"responseLength" : 5159,
"protocol" : "op_query",
"millis" : 13477,
"ts" : ISODate("2022-04-11T05:46:33.221Z"),
"client" : "127.0.0.1",
"allUsers" : [ ],
"user" : ""
}
and the explain information is as follows:
{
"queryPlanner" : {
"plannerVersion" : 1,
"namespace" : "sns.message",
"indexFilterSet" : false,
"parsedQuery" : {
"$and" : [
{
"$or" : [
{
"fromid" : {
"$eq" : 2
}
},
{
"rid" : {
"$eq" : 2
}
},
{
"validtime" : {
"$eq" : null
}
},
{
"validtime" : {
"$lt" : 1649565335
}
}
]
},
{
"toid" : {
"$eq" : 123
}
}
]
},
"winningPlan" : {
"stage" : "LIMIT",
"limitAmount" : 10,
"inputStage" : {
"stage" : "FETCH",
"filter" : {
"$and" : [
{
"$or" : [
{
"fromid" : {
"$eq" : 2
}
},
{
"rid" : {
"$eq" : 2
}
},
{
"validtime" : {
"$eq" : null
}
},
{
"validtime" : {
"$lt" : 1649565335
}
}
]
},
{
"toid" : {
"$eq" : 123
}
}
]
},
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"sortid" : 1,
"toid" : 1
},
"indexName" : "toid_1_sortid_1",
"isMultiKey" : false,
"multiKeyPaths" : {
"sortid" : [ ],
"toid" : [ ]
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 1,
"direction" : "backward",
"indexBounds" : {
"sortid" : [
"[MaxKey, MinKey]"
],
"toid" : [
"[MaxKey, MinKey]"
]
}
}
}
},
"rejectedPlans" : [
{
"stage" : "SORT",
"sortPattern" : {
"sortid" : -1
},
"memLimit" : 104857600,
"limitAmount" : 10,
"type" : "simple",
"inputStage" : {
"stage" : "FETCH",
"filter" : {
"$or" : [
{
"fromid" : {
"$eq" : 2
}
},
{
"rid" : {
"$eq" : 2
}
},
{
"validtime" : {
"$eq" : null
}
},
{
"validtime" : {
"$lt" : 1649565335
}
}
]
},
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"toid" : 1
},
"indexName" : "toid_1",
"isMultiKey" : false,
"multiKeyPaths" : {
"toid" : [ ]
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "forward",
"indexBounds" : {
"toid" : [
"[123.0, 123.0]"
]
}
}
}
},
{
"stage" : "SORT",
"sortPattern" : {
"sortid" : -1
},
"memLimit" : 104857600,
"limitAmount" : 10,
"type" : "simple",
"inputStage" : {
"stage" : "FETCH",
"filter" : {
"$or" : [
{
"fromid" : {
"$eq" : 2
}
},
{
"rid" : {
"$eq" : 2
}
},
{
"validtime" : {
"$eq" : null
}
},
{
"validtime" : {
"$lt" : 1649565335
}
}
]
},
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"toid" : 1,
"replyid" : 1
},
"indexName" : "toid_1_replyid_1",
"isMultiKey" : false,
"multiKeyPaths" : {
"toid" : [ ],
"replyid" : [ ]
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 1,
"direction" : "forward",
"indexBounds" : {
"toid" : [
"[123.0, 123.0]"
],
"replyid" : [
"[MinKey, MaxKey]"
]
}
}
}
},
{
"stage" : "SORT",
"sortPattern" : {
"sortid" : -1
},
"memLimit" : 104857600,
"limitAmount" : 10,
"type" : "simple",
"inputStage" : {
"stage" : "FETCH",
"filter" : {
"$or" : [
{
"fromid" : {
"$eq" : 2
}
},
{
"rid" : {
"$eq" : 2
}
},
{
"validtime" : {
"$eq" : null
}
},
{
"validtime" : {
"$lt" : 1649565335
}
}
]
},
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"toid" : 1,
"validtime" : 1,
"time" : 1
},
"indexName" : "toid_1_validtime_1_time_1",
"isMultiKey" : false,
"multiKeyPaths" : {
"toid" : [ ],
"validtime" : [ ],
"time" : [ ]
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 1,
"direction" : "forward",
"indexBounds" : {
"toid" : [
"[123.0, 123.0]"
],
"validtime" : [
"[MinKey, MaxKey]"
],
"time" : [
"[MinKey, MaxKey]"
]
}
}
}
},
// omit...
]
},
"executionStats" : {
"executionSuccess" : true,
"nReturned" : 10,
"executionTimeMillis" : 13477,
"totalKeysExamined" : 1295456,
"totalDocsExamined" : 1295456,
"executionStages" : {
"stage" : "LIMIT",
"nReturned" : 10,
"executionTimeMillisEstimate" : 700,
"works" : 1295457,
"advanced" : 10,
"needTime" : 1295446,
"needYield" : 0,
"saveState" : 6477,
"restoreState" : 6477,
"isEOF" : 1,
"limitAmount" : 10,
"inputStage" : {
"stage" : "FETCH",
"filter" : {
"$and" : [
{
"$or" : [
{
"fromid" : {
"$eq" : 2
}
},
{
"rid" : {
"$eq" : 2
}
},
{
"validtime" : {
"$eq" : null
}
},
{
"validtime" : {
"$lt" : 1649565335
}
}
]
},
{
"toid" : {
"$eq" : 123
}
}
]
},
"nReturned" : 10,
"executionTimeMillisEstimate" : 690,
"works" : 1295456,
"advanced" : 10,
"needTime" : 1295446,
"needYield" : 0,
"saveState" : 6477,
"restoreState" : 6477,
"isEOF" : 0,
"docsExamined" : 1295456,
"alreadyHasObj" : 0,
"inputStage" : {
"stage" : "IXSCAN",
"nReturned" : 1295456,
"executionTimeMillisEstimate" : 188,
"works" : 1295456,
"advanced" : 1295456,
"needTime" : 0,
"needYield" : 0,
"saveState" : 6477,
"restoreState" : 6477,
"isEOF" : 0,
"keyPattern" : {
"sortid" : 1,
"toid" : 1
},
"indexName" : "toid_1_sortid_1",
"isMultiKey" : false,
"multiKeyPaths" : {
"sortid" : [ ],
"toid" : [ ]
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 1,
"direction" : "backward",
"indexBounds" : {
"sortid" : [
"[MaxKey, MinKey]"
],
"toid" : [
"[MaxKey, MinKey]"
]
},
"keysExamined" : 1295456,
"seeks" : 1,
"dupsTested" : 0,
"dupsDropped" : 0
}
}
}
},
"serverInfo" : {
"host" : "MongoDB",
"port" : 22005,
"version" : "4.4.13",
"gitVersion" : "df25c71b8674a78e17468f48bcda5285decb9246"
},
"ok" : 1,
"$gleStats" : {
"lastOpTime" : Timestamp(0, 0),
"electionId" : ObjectId("7fffffff0000000000000001")
},
"lastCommittedOpTime" : Timestamp(1649655993, 20),
"$configServerState" : {
"opTime" : {
"ts" : Timestamp(1649655992, 11),
"t" : NumberLong(1)
}
},
"$clusterTime" : {
"clusterTime" : Timestamp(1649655993, 26),
"signature" : {
"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
"keyId" : NumberLong(0)
}
},
"operationTime" : Timestamp(1649655993, 26)
}
in the "executionStats" block, both the "LIMIT" stage and its substage take less than or equal to 700ms, but the total time is 13477ms. what happened during this time? How can I locate the reason for the time consuming? And Looking forward to optimization suggestions.
Thanks
The total executionTimeMillis is the "total" amount of time required for both planning + execution, this means there are certain factors now calculated in the breakdown per stage.
For example:
Lock acquisition / overall db load.
When planning only a 100 document sample is examined, however as you can see for the actual query over 1milion documents are being fetched. If the there are other operations on the same collection that lock it or if the db is in general under heavy load this can affect performance as your query is waiting in line for resources.
Disk latency
As you can see your docsExamined is equal to keysExamined, this means that every single documents that matched the query needed to be fetched from disk to be examined.
This is quite a lot of overhead. and I suspect this is the main "issue" regarding performance. You could probably lower this by creating a proper compound index to satisfy this query.
You can also add a disk with more IOPS, boosting hardware will always increase performance, especially if it's at the bottleneck places.

MongoDB is not using the right index

please see below query which is supposed to use compound_index but MongoDB opt in to use id index which is a default index.
db.getCollection('tmp_properties').find(
{
mls:"mls_name",
$or : [
{is_custom_listing : false},
{is_custom_listing : {$exists : false}}
],
listing_status : {$in : ['Sold', 'Rented']}
},
{
id : 1
}
).limit(1000).sort({_id:-1}).explain();
Here is my index:
compound_index
{
"mls" : 1.0,
"is_custom_listing" : 1.0,
"listing_status" : 1.0,
"id" : 1.0,
"_id" : 1.0
}
Explain
/* 1 */
{
"queryPlanner" : {
"plannerVersion" : 1,
"namespace" : "tmp_properties",
"indexFilterSet" : false,
"parsedQuery" : {
"$and" : [
{
"$or" : [
{
"is_custom_listing" : {
"$eq" : false
}
},
{
"$nor" : [
{
"is_custom_listing" : {
"$exists" : true
}
}
]
}
]
},
{
"mls" : {
"$eq" : "crmls"
}
},
{
"listing_status" : {
"$in" : [
"Rented",
"Sold"
]
}
}
]
},
"winningPlan" : {
"stage" : "LIMIT",
"limitAmount" : 1000,
"inputStage" : {
"stage" : "PROJECTION",
"transformBy" : {
"id" : 1.0
},
"inputStage" : {
"stage" : "FETCH",
"filter" : {
"$and" : [
{
"$or" : [
{
"is_custom_listing" : {
"$eq" : false
}
},
{
"$nor" : [
{
"is_custom_listing" : {
"$exists" : true
}
}
]
}
]
},
{
"mls" : {
"$eq" : "crmls"
}
},
{
"listing_status" : {
"$in" : [
"Rented",
"Sold"
]
}
}
]
},
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"_id" : 1
},
"indexName" : "_id_",
"isMultiKey" : false,
"multiKeyPaths" : {
"_id" : []
},
"isUnique" : true,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "backward",
"indexBounds" : {
"_id" : [
"[MaxKey, MinKey]"
]
}
}
}
}
},
"rejectedPlans" : [
{
"stage" : "PROJECTION",
"transformBy" : {
"id" : 1.0
},
"inputStage" : {
"stage" : "SORT",
"sortPattern" : {
"_id" : -1.0
},
"limitAmount" : 1000,
"inputStage" : {
"stage" : "SORT_KEY_GENERATOR",
"inputStage" : {
"stage" : "FETCH",
"inputStage" : {
"stage" : "OR",
"inputStages" : [
{
"stage" : "IXSCAN",
"keyPattern" : {
"mls" : 1.0,
"is_custom_listing" : 1.0,
"listing_status" : 1.0,
"id" : 1.0,
"_id" : 1.0
},
"indexName" : "compound_index",
"isMultiKey" : false,
"multiKeyPaths" : {
"mls" : [],
"is_custom_listing" : [],
"listing_status" : [],
"id" : [],
"_id" : []
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "forward",
"indexBounds" : {
"mls" : [
"[\"crmls\", \"crmls\"]"
],
"is_custom_listing" : [
"[false, false]"
],
"listing_status" : [
"[\"Rented\", \"Rented\"]",
"[\"Sold\", \"Sold\"]"
],
"id" : [
"[MinKey, MaxKey]"
],
"_id" : [
"[MinKey, MaxKey]"
]
}
},
{
"stage" : "FETCH",
"filter" : {
"$nor" : [
{
"is_custom_listing" : {
"$exists" : true
}
}
]
},
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"mls" : 1.0,
"is_custom_listing" : 1.0,
"listing_status" : 1.0,
"id" : 1.0,
"_id" : 1.0
},
"indexName" : "compound_index",
"isMultiKey" : false,
"multiKeyPaths" : {
"mls" : [],
"is_custom_listing" : [],
"listing_status" : [],
"id" : [],
"_id" : []
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "forward",
"indexBounds" : {
"mls" : [
"[\"crmls\", \"crmls\"]"
],
"is_custom_listing" : [
"[null, null]"
],
"listing_status" : [
"[\"Rented\", \"Rented\"]",
"[\"Sold\", \"Sold\"]"
],
"id" : [
"[MinKey, MaxKey]"
],
"_id" : [
"[MinKey, MaxKey]"
]
}
}
}
]
}
}
}
}
},
{
"stage" : "PROJECTION",
"transformBy" : {
"id" : 1.0
},
"inputStage" : {
"stage" : "SORT",
"sortPattern" : {
"_id" : -1.0
},
"limitAmount" : 1000,
"inputStage" : {
"stage" : "SORT_KEY_GENERATOR",
"inputStage" : {
"stage" : "FETCH",
"filter" : {
"$or" : [
{
"is_custom_listing" : {
"$eq" : false
}
},
{
"$nor" : [
{
"is_custom_listing" : {
"$exists" : true
}
}
]
}
]
},
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"mls" : 1.0,
"is_custom_listing" : 1.0,
"listing_status" : 1.0,
"id" : 1.0,
"_id" : 1.0
},
"indexName" : "compound_index",
"isMultiKey" : false,
"multiKeyPaths" : {
"mls" : [],
"is_custom_listing" : [],
"listing_status" : [],
"id" : [],
"_id" : []
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "forward",
"indexBounds" : {
"mls" : [
"[\"crmls\", \"crmls\"]"
],
"is_custom_listing" : [
"[MinKey, MaxKey]"
],
"listing_status" : [
"[\"Rented\", \"Rented\"]",
"[\"Sold\", \"Sold\"]"
],
"id" : [
"[MinKey, MaxKey]"
],
"_id" : [
"[MinKey, MaxKey]"
]
}
}
}
}
}
}
]
}
"ok" : 1.0
}
UPDATE
After I change index order to:
{
"mls" : 1,
"is_custom_listing" : 1,
"listing_status" : 1,
"_id" : 1,
"id" : 1
}
I got following explain output:
/* 1 */
{
"queryPlanner" : {
"plannerVersion" : 1,
"namespace" : "tmp_properties",
"indexFilterSet" : false,
"parsedQuery" : {
"$and" : [
{
"$or" : [
{
"is_custom_listing" : {
"$eq" : false
}
},
{
"$nor" : [
{
"is_custom_listing" : {
"$exists" : true
}
}
]
}
]
},
{
"mls" : {
"$eq" : "crmls"
}
},
{
"listing_status" : {
"$in" : [
"Rented",
"Sold"
]
}
}
]
},
"winningPlan" : {
"stage" : "LIMIT",
"limitAmount" : 1000,
"inputStage" : {
"stage" : "PROJECTION",
"transformBy" : {
"id" : 1.0
},
"inputStage" : {
"stage" : "FETCH",
"filter" : {
"$and" : [
{
"$or" : [
{
"is_custom_listing" : {
"$eq" : false
}
},
{
"$nor" : [
{
"is_custom_listing" : {
"$exists" : true
}
}
]
}
]
},
{
"mls" : {
"$eq" : "crmls"
}
},
{
"listing_status" : {
"$in" : [
"Rented",
"Sold"
]
}
}
]
},
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"_id" : 1
},
"indexName" : "_id_",
"isMultiKey" : false,
"multiKeyPaths" : {
"_id" : []
},
"isUnique" : true,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "forward",
"indexBounds" : {
"_id" : [
"[MinKey, MaxKey]"
]
}
}
}
}
},
"rejectedPlans" : [
{
"stage" : "PROJECTION",
"transformBy" : {
"id" : 1.0
},
"inputStage" : {
"stage" : "SORT",
"sortPattern" : {
"_id" : 1.0
},
"limitAmount" : 1000,
"inputStage" : {
"stage" : "SORT_KEY_GENERATOR",
"inputStage" : {
"stage" : "FETCH",
"inputStage" : {
"stage" : "OR",
"inputStages" : [
{
"stage" : "IXSCAN",
"keyPattern" : {
"mls" : 1.0,
"is_custom_listing" : 1.0,
"listing_status" : 1.0,
"_id" : 1.0,
"id" : 1.0
},
"indexName" : "compound_index",
"isMultiKey" : false,
"multiKeyPaths" : {
"mls" : [],
"is_custom_listing" : [],
"listing_status" : [],
"_id" : [],
"id" : []
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "forward",
"indexBounds" : {
"mls" : [
"[\"crmls\", \"crmls\"]"
],
"is_custom_listing" : [
"[false, false]"
],
....
}
},
{
"stage" : "FETCH",
"filter" : {
"$nor" : [
{
"is_custom_listing" : {
"$exists" : true
}
}
]
},
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"mls" : 1.0,
"is_custom_listing" : 1.0,
"listing_status" : 1.0,
"_id" : 1.0,
"id" : 1.0
},
"indexName" : "compound_index",
"isMultiKey" : false,
"multiKeyPaths" : {
"mls" : [],
"is_custom_listing" : [],
"listing_status" : [],
"_id" : [],
"id" : []
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "forward",
"indexBounds" : {
"mls" : [
"[\"crmls\", \"crmls\"]"
],
....
}
}
}
]
}
}
}
}
},
{
"stage" : "PROJECTION",
"transformBy" : {
"id" : 1.0
},
"inputStage" : {
"stage" : "SORT",
"sortPattern" : {
"_id" : 1.0
},
"limitAmount" : 1000,
"inputStage" : {
"stage" : "SORT_KEY_GENERATOR",
"inputStage" : {
"stage" : "FETCH",
"filter" : {
"$or" : [
{
"is_custom_listing" : {
"$eq" : false
}
},
{
"$nor" : [
{
"is_custom_listing" : {
"$exists" : true
}
}
]
}
]
},
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"mls" : 1.0,
"is_custom_listing" : 1.0,
"listing_status" : 1.0,
"_id" : 1.0,
"id" : 1.0
},
"indexName" : "compound_index",
"isMultiKey" : false,
"multiKeyPaths" : {
"mls" : [],
"is_custom_listing" : [],
"listing_status" : [],
"_id" : [],
"id" : []
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "forward",
"indexBounds" : {
"mls" : [
"[\"crmls\", \"crmls\"]"
],
....
}
}
}
}
}
}
]
}
}
Notice that the rejected plan using the compound index required an in-memory sort. If you run the explain with the "allPlansExecution" option, you can compare the performance of each.
The main difference between the considered index is the index on {_id:1} supports sorting the results without an in-memory sort stage.
The compound index lists the equality matched fields first, then the unfiltered id field, and finally the sort field.
If you rebuild that index to follow the ESR rule, this query might both avoid the in-memory sort, and be fully covered by the index. i.e.:
{
"mls" : 1,
"_id" : 1,
"is_custom_listing" : 1,
"listing_status" : 1,
"id" : 1
}

MongoDB (text search with the relevant field) aggregation problem

I have the MongoDB aggregation query
db.data.aggregate([{ "$match" : { "$text" : { "$search" : "STORAGE TYPE" } } },
{ "$group" :
{ "_id" :{"doc_type": "$doc_type" ,"title" : "$title", "player_name" : "$player_name", "player_type" : "INSTITUTION", "country_code" :"$country_code" },
"number_records" : { "$sum" : 1}
}
},
{"$match" : {"doc_type": "PATENT"} },
{"$sort":{"number_records" : -1}},
{"$limit" : 10}],
{"allowDiskuse" : true}
)
When I tried to execute the above code, it keeps on buffering for a long time, I am not getting any output. Can anyone help me?
When I used command explain(), it shows the following code:
{
"stages" : [
{
"$cursor" : {
"query" : {
"$and" : [
{
"$text" : {
"$search" : "STORAGE TYPE"
}
},
{
"doc_type" : "PATENT"
}
]
},
"fields" : {
"country_code" : 1,
"doc_type" : 1,
"player_name" : 1,
"title" : 1,
"_id" : 0
},
"queryPlanner" : {
"plannerVersion" : 1,
"namespace" : "datadocuments.data",
"indexFilterSet" : false,
"parsedQuery" : {
"$and" : [
{
"doc_type" : {
"$eq" : "PATENT"
}
},
{
"$text" : {
"$search" : "STORAGE TYPE",
"$language" : "english",
"$caseSensitive" : false,
"$diacriticSensitive" : false
}
}
]
},
"winningPlan" : {
"stage" : "FETCH",
"filter" : {
"doc_type" : {
"$eq" : "PATENT"
}
},
"inputStage" : {
"stage" : "TEXT",
"indexPrefix" : {
},
"indexName" : "title",
"parsedTextQuery" : {
"terms" : [
"storag",
"type"
],
"negatedTerms" : [ ],
"phrases" : [ ],
"negatedPhrases" : [ ]
},
"textIndexVersion" : 3,
"inputStage" : {
"stage" : "TEXT_MATCH",
"inputStage" : {
"stage" : "FETCH",
"inputStage" : {
"stage" : "OR",
"inputStages" : [
{
"stage" : "IXSCAN",
"keyPattern" : {
"_fts" : "text",
"_ftsx" : 1
},
"indexName" : "title",
"isMultiKey" : true,
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "backward",
"indexBounds" : {
}
},
{
"stage" : "IXSCAN",
"keyPattern" : {
"_fts" : "text",
"_ftsx" : 1
},
"indexName" : "title",
"isMultiKey" : true,
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "backward",
"indexBounds" : {
}
}
]
}
}
}
}
},
"rejectedPlans" : [ ]
}
}
},
{
"$group" : {
"_id" : {
"doc_type" : "$doc_type",
"title" : "$title",
"player_name" : "$player_name",
"player_type" : {
"$const" : "INSTITUTION"
},
"country_code" : "$country_code"
},
"number_records" : {
"$sum" : {
"$const" : 1
}
}
}
},
{
"$sort" : {
"sortKey" : {
"number_records" : -1
},
"limit" : NumberLong("10")
}
}
],
"ok" : 1
}
I couldn't figure out the mistake; is there any problem in aggregation, if not, how to increase the performance?
Your error comes from your second match stage : at this point, doc_type doesn't exist, but _id.doc_type instead. But you bettermerge this stage with the first one, to improve performance by reducing number of documents passed to the $group stage.
Your improved query will be :
db.data.aggregate([
{"$match" : { "$text" : { "$search" : "STORAGE TYPE" `},"doc_type": "PATENT" } },`
{ "$group" :
{ "_id" :{"doc_type": "$doc_type" ,"title" : "$title", "player_name" : "$player_name", "player_type" : "INSTITUTION", "country_code" :"$country_code" },
"number_records" : { "$sum" : 1}
}
},
{"$sort":{"number_records" : -1}},
{"$limit" : 10}],
{"allowDiskuse" : true}
)

Why is mongodb not using full index

I have a collection with one 4 key compound index:
> db.event.getIndexes()
[
{
"v" : 2,
"key" : {
"_id" : 1
},
"name" : "_id_",
},
{
"v" : 2,
"key" : {
"epochWID" : 1,
"category" : 1,
"mos.types" : 1,
"mos.name" : 1
},
"name" : "epochWID_category_motype_movalue",
}
]
Query is as follows:
> db.event.explain().find({ "epochWID": 1510456188087, "category": 6, "mos.types": 9, "mos.name": "ctx_1" })
{
"queryPlanner" : {
"plannerVersion" : 1,
"indexFilterSet" : false,
"parsedQuery" : {
"$and" : [
{
"category" : {
"$eq" : 6
}
},
{
"epochWID" : {
"$eq" : 1510456188087
}
},
{
"mos.name" : {
"$eq" : "ctx_1"
}
},
{
"mos.types" : {
"$eq" : 9
}
}
]
},
"winningPlan" : {
"stage" : "FETCH",
"filter" : {
"mos.name" : {
"$eq" : "ctx_1"
}
},
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"epochWID" : 1,
"category" : 1,
"mos.types" : 1,
"mos.name" : 1
},
"indexName" : "epochWID_category_motype_movalue",
"isMultiKey" : true,
"multiKeyPaths" : {
"epochWID" : [ ],
"category" : [ ],
"mos.types" : [
"mos",
"mos.types"
],
"mos.name" : [
"mos"
]
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "forward",
"indexBounds" : {
"epochWID" : [
"[1510456188087.0, 1510456188087.0]"
],
"category" : [
"[6.0, 6.0]"
],
"mos.types" : [
"[9.0, 9.0]"
],
"mos.name" : [
"[MinKey, MaxKey]"
]
}
}
},
"rejectedPlans" : [ ]
},
"serverInfo" : {
"version" : "3.4.9",
},
"ok" : 1
}
Now if you look at the plan's indexBounds: it uses the first 3 keys but not the 4th mos.name, why?
"indexBounds" : {
"epochWID" : [
"[1510456188087.0, 1510456188087.0]"
],
"category" : [
"[6.0, 6.0]"
],
"mos.types" : [
"[9.0, 9.0]"
],
"mos.name" : [
"[MinKey, MaxKey]"
]
}
Based on https://docs.mongodb.com/manual/core/index-multikey/#compound-multikey-indexes we need to use $elemMatch, so following query uses the full index
> db.event.explain().find({ "epochWID": 1510456188087, "category": 6, "mos": { $elemMatch: {"types": 9, "name": "ctx_1"} } })
{
"queryPlanner" : {
"plannerVersion" : 1,
"indexFilterSet" : false,
"parsedQuery" : {
"$and" : [
{
"mos" : {
"$elemMatch" : {
"$and" : [
{
"name" : {
"$eq" : "ctx_1"
}
},
{
"types" : {
"$eq" : 9
}
}
]
}
}
},
{
"category" : {
"$eq" : 6
}
},
{
"epochWID" : {
"$eq" : 1510456188087
}
}
]
},
"winningPlan" : {
"stage" : "FETCH",
"filter" : {
"mos" : {
"$elemMatch" : {
"$and" : [
{
"types" : {
"$eq" : 9
}
},
{
"name" : {
"$eq" : "ctx_1"
}
}
]
}
}
},
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"epochWID" : 1,
"category" : 1,
"mos.types" : 1,
"mos.name" : 1
},
"indexName" : "epochWID_category_motype_movalue",
"isMultiKey" : true,
"multiKeyPaths" : {
"epochWID" : [ ],
"category" : [ ],
"mos.types" : [
"mos",
"mos.types"
],
"mos.name" : [
"mos"
]
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "forward",
"indexBounds" : {
"epochWID" : [
"[1510456188087.0, 1510456188087.0]"
],
"category" : [
"[6.0, 6.0]"
],
"mos.types" : [
"[9.0, 9.0]"
],
"mos.name" : [
"[\"ctx_1\", \"ctx_1\"]"
]
}
}
},
"rejectedPlans" : [ ]
},
"serverInfo" : {
"version" : "3.4.9",
},
"ok" : 1
}
EDIT: I contacted MongoDb support. Regarding multi key indexes and array fields - tl;dr is - An index is fine as long as only one of the indexed fields ever contains an array value (which is true in my case). Nesting level doesn't matter. The problem is indeed parallel arrays due to need of cartesian product.
A multi-key index cannot be across multiple arrays within a document. See the Limitations and reasoning in the documentation https://docs.mongodb.com/manual/core/index-multikey/#compound-multikey-indexes

Mongo date range index with filters

We have the below query
db.Comment.find(
{
$and: [
{ reportCount: { $gt: 0 } },
{ assignee: { $exists: false } },
{ creationDate: { $gt: new Date(1507831097809) } },
{ creationDate: { $lt: new Date(1508522297966) } },
{ siteId: 'MAIN' },
{ parent: { $exists: false } },
{ status: 'ACTIVE' }
]
})
.sort({ creationDate: 1 })
And we have an index
{
"v" : 2,
"key" : {
"creationDate" : 1,
"reportCount" : 1,
"label" : 1
}
}
Here are explain results:
{
"queryPlanner" : {
"plannerVersion" : 1,
"namespace" : "myNameSpace",
"indexFilterSet" : false,
"parsedQuery" : {
"$and" : [
{
"siteId" : {
"$eq" : "MAIN"
}
},
{
"status" : {
"$eq" : "ACTIVE"
}
},
{
"creationDate" : {
"$lt" : ISODate("2017-10-20T17:58:17.966Z")
}
},
{
"creationDate" : {
"$gt" : ISODate("2017-10-12T17:58:17.809Z")
}
},
{
"reportCount" : {
"$gt" : 0.0
}
},
{
"$nor" : [
{
"assignee" : {
"$exists" : true
}
}
]
},
{
"$nor" : [
{
"parent" : {
"$exists" : true
}
}
]
}
]
},
"winningPlan" : {
"stage" : "FETCH",
"filter" : {
"$and" : [
{
"siteId" : {
"$eq" : "MAIN"
}
},
{
"status" : {
"$eq" : "ACTIVE"
}
},
{
"$nor" : [
{
"assignee" : {
"$exists" : true
}
}
]
},
{
"$nor" : [
{
"parent" : {
"$exists" : true
}
}
]
}
]
},
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"creationDate" : 1.0,
"reportCount" : 1.0,
"label" : 1.0
},
"indexName" : "creationDate_1_reportCount_1_label_1",
"isMultiKey" : false,
"multiKeyPaths" : {
"creationDate" : [],
"reportCount" : [],
"label" : []
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "forward",
"indexBounds" : {
"creationDate" : [
"(new Date(1507831097809), new Date(1508522297966))"
],
"reportCount" : [
"(0.0, inf.0]"
],
"label" : [
"[MinKey, MaxKey]"
]
}
}
},
"rejectedPlans" : [
{
"stage" : "SORT",
"sortPattern" : {
"creationDate" : 1.0
},
"inputStage" : {
"stage" : "SORT_KEY_GENERATOR",
"inputStage" : {
"stage" : "FETCH",
"filter" : {
"$and" : [
{
"$nor" : [
{
"parent" : {
"$exists" : true
}
}
]
},
{
"siteId" : {
"$eq" : "MAIN"
}
},
{
"status" : {
"$eq" : "ACTIVE"
}
},
{
"creationDate" : {
"$lt" : ISODate("2017-10-20T17:58:17.966Z")
}
},
{
"creationDate" : {
"$gt" : ISODate("2017-10-12T17:58:17.809Z")
}
},
{
"reportCount" : {
"$gt" : 0.0
}
},
{
"$nor" : [
{
"assignee" : {
"$exists" : true
}
}
]
}
]
},
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"parent" : 1.0
},
"indexName" : "parent_1",
"isMultiKey" : false,
"multiKeyPaths" : {
"parent" : []
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "forward",
"indexBounds" : {
"parent" : [
"[null, null]"
]
}
}
}
}
},
{
"stage" : "SORT",
"sortPattern" : {
"creationDate" : 1.0
},
"inputStage" : {
"stage" : "SORT_KEY_GENERATOR",
"inputStage" : {
"stage" : "FETCH",
"filter" : {
"$and" : [
{
"$nor" : [
{
"assignee" : {
"$exists" : true
}
}
]
},
{
"siteId" : {
"$eq" : "MAIN"
}
},
{
"status" : {
"$eq" : "ACTIVE"
}
},
{
"creationDate" : {
"$lt" : ISODate("2017-10-20T17:58:17.966Z")
}
},
{
"creationDate" : {
"$gt" : ISODate("2017-10-12T17:58:17.809Z")
}
},
{
"reportCount" : {
"$gt" : 0.0
}
},
{
"$nor" : [
{
"parent" : {
"$exists" : true
}
}
]
}
]
},
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"assignee" : 1.0
},
"indexName" : "assignee_1",
"isMultiKey" : false,
"multiKeyPaths" : {
"assignee" : []
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "forward",
"indexBounds" : {
"assignee" : [
"[null, null]"
]
}
}
}
}
},
{
"stage" : "SORT",
"sortPattern" : {
"creationDate" : 1.0
},
"inputStage" : {
"stage" : "SORT_KEY_GENERATOR",
"inputStage" : {
"stage" : "FETCH",
"filter" : {
"$and" : [
{
"status" : {
"$eq" : "ACTIVE"
}
},
{
"creationDate" : {
"$lt" : ISODate("2017-10-20T17:58:17.966Z")
}
},
{
"creationDate" : {
"$gt" : ISODate("2017-10-12T17:58:17.809Z")
}
},
{
"reportCount" : {
"$gt" : 0.0
}
},
{
"$nor" : [
{
"assignee" : {
"$exists" : true
}
}
]
},
{
"$nor" : [
{
"parent" : {
"$exists" : true
}
}
]
}
]
},
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"siteId" : 1.0,
"updatedDate" : 1.0,
"label" : 1.0
},
"indexName" : "siteId_1_updatedDate_1_label_1",
"isMultiKey" : false,
"multiKeyPaths" : {
"siteId" : [],
"updatedDate" : [],
"label" : []
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "forward",
"indexBounds" : {
"siteId" : [
"[\"MAIN\", \"MAIN\"]"
],
"updatedDate" : [
"[MinKey, MaxKey]"
],
"label" : [
"[MinKey, MaxKey]"
]
}
}
}
}
},
{
"stage" : "SORT",
"sortPattern" : {
"creationDate" : 1.0
},
"inputStage" : {
"stage" : "SORT_KEY_GENERATOR",
"inputStage" : {
"stage" : "FETCH",
"filter" : {
"$and" : [
{
"$nor" : [
{
"parent" : {
"$exists" : true
}
}
]
},
{
"$nor" : [
{
"assignee" : {
"$exists" : true
}
}
]
},
{
"siteId" : {
"$eq" : "MAIN"
}
},
{
"status" : {
"$eq" : "ACTIVE"
}
},
{
"creationDate" : {
"$lt" : ISODate("2017-10-20T17:58:17.966Z")
}
},
{
"creationDate" : {
"$gt" : ISODate("2017-10-12T17:58:17.809Z")
}
},
{
"reportCount" : {
"$gt" : 0.0
}
}
]
},
"inputStage" : {
"stage" : "AND_SORTED",
"inputStages" : [
{
"stage" : "IXSCAN",
"keyPattern" : {
"parent" : 1.0
},
"indexName" : "parent_1",
"isMultiKey" : false,
"multiKeyPaths" : {
"parent" : []
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "forward",
"indexBounds" : {
"parent" : [
"[null, null]"
]
}
},
{
"stage" : "IXSCAN",
"keyPattern" : {
"assignee" : 1.0
},
"indexName" : "assignee_1",
"isMultiKey" : false,
"multiKeyPaths" : {
"assignee" : []
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "forward",
"indexBounds" : {
"assignee" : [
"[null, null]"
]
}
}
]
}
}
}
}
]
},
"executionStats" : {
"executionSuccess" : true,
"nReturned" : 19,
"executionTimeMillis" : 8,
"totalKeysExamined" : 533,
"totalDocsExamined" : 56,
"executionStages" : {
"stage" : "FETCH",
"filter" : {
"$and" : [
{
"siteId" : {
"$eq" : "MAIN"
}
},
{
"status" : {
"$eq" : "ACTIVE"
}
},
{
"$nor" : [
{
"assignee" : {
"$exists" : true
}
}
]
},
{
"$nor" : [
{
"parent" : {
"$exists" : true
}
}
]
}
]
},
"nReturned" : 19,
"executionTimeMillisEstimate" : 0,
"works" : 534,
"advanced" : 19,
"needTime" : 513,
"needYield" : 0,
"saveState" : 20,
"restoreState" : 20,
"isEOF" : 1,
"invalidates" : 0,
"docsExamined" : 56,
"alreadyHasObj" : 0,
"inputStage" : {
"stage" : "IXSCAN",
"nReturned" : 56,
"executionTimeMillisEstimate" : 0,
"works" : 533,
"advanced" : 56,
"needTime" : 476,
"needYield" : 0,
"saveState" : 20,
"restoreState" : 20,
"isEOF" : 1,
"invalidates" : 0,
"keyPattern" : {
"creationDate" : 1.0,
"reportCount" : 1.0,
"label" : 1.0
},
"indexName" : "creationDate_1_reportCount_1_label_1",
"isMultiKey" : false,
"multiKeyPaths" : {
"creationDate" : [],
"reportCount" : [],
"label" : []
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "forward",
"indexBounds" : {
"creationDate" : [
"(new Date(1507831097809), new Date(1508522297966))"
],
"reportCount" : [
"(0.0, inf.0]"
],
"label" : [
"[MinKey, MaxKey]"
]
},
"keysExamined" : 533,
"seeks" : 477,
"dupsTested" : 0,
"dupsDropped" : 0,
"seenInvalidated" : 0
}
}
},
"ok" : 1.0
}
The query is still taking 700-800 ms to return the data. How can I change the index to make the query run faster? Don't consider "keysExamined" : 533, "seeks" : 477, This data. This is just test data.
Looks like its using an index but only the first field in the index? Also multuKey is false?
A few key points from the explain plan output:
The query addresses the following attributes: siteId, status, creationDate, reportCount, assignee, parent
The winning plan has two stages:
IX_SCAN uses creationDate_1_reportCount_1_label_1, this uses indexed lookups on creationDate and reportCount to identify 56 documents which are then forwarded to the FETCH stage
FETCH receives 56 documents from the IX_SCAN stage and then interrogates these documents to apply the siteId, status, assignee and parent filters. This interrogation causes 37 documents to be discarded resulting in 19 document to be returned.
So, your index covers just 2 of the 6 attributes in your query and the remaining 4 attributes in your query are applied by examining the documents not the index. If you want this query to be fully index covered then create the following index:
db.collection.createIndex(
{siteId: 1, status: 1, creationDate: 1, reportCount: 1, assignee: 1, parent: 1}
)
If you re run with this index in place then you should find that (a) MongoDB chooses this index and (b) the number of documents forwarded by the IX_SCAN stage is the same as the number of documents returned by your find call.
I say "should find" because there are other aspects here which might result in MongoDB choosing a different index e.g. use of $nor and the sort stage (creationDate: 1). I would recommend tweaking the index and running with explain 'on' after each tweak and looking for these key items in the executionStats sub document:
"nReturned"
"totalKeysExamined"
"totalDocsExamined"
A simple rule of thumb is this: the closer totalKeysExamined is to nReturned and the closer totalDocsExamined is to zero ... the better your index coverage.
There is also the question of the cost of an index (in terms of impact on write times and index storage) so I'd suggest considering your non functional requirements - can your desired elapsed times be achieved without full index coverage? If not, then you should proceed with empirical testing but be prepared to tweak your choice in reponse to what the explain() output tells you.