MongoDB is not using the right index - mongodb

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
}

Related

Compound MongoDB index not used for exact same query

I have index like this: businessId_1_accountId_1_dataHash_1_deleted_1
And query like this, does not use it, but uses another one. I can't understand why.
db.getCollection("transactions").find({
"businessId": ObjectId("62c56bbdba7f1d001368f217"),
"accountId": ObjectId("62c56bbef2c6530d4a9b5976"),
"dataHash": "00002478a5c9594923833b3534b2d0b17bb7298a",
deleted: { $ne: true }
}, { _id: true }).explain();
Here is the explain result
{
"explainVersion" : "1",
"queryPlanner" : {
"namespace" : "stage.transactions",
"indexFilterSet" : false,
"parsedQuery" : {
"$and" : [
{
"accountId" : {
"$eq" : ObjectId("62c56bbef2c6530d4a9b5976")
}
},
{
"businessId" : {
"$eq" : ObjectId("62c56bbdba7f1d001368f217")
}
},
{
"dataHash" : {
"$eq" : "00002478a5c9594923833b3534b2d0b17bb7298a"
}
},
{
"deleted" : {
"$not" : {
"$eq" : true
}
}
}
]
},
"queryHash" : "29075D21",
"planCacheKey" : "7C80754C",
"maxIndexedOrSolutionsReached" : false,
"maxIndexedAndSolutionsReached" : false,
"maxScansToExplodeReached" : false,
"winningPlan" : {
"stage" : "PROJECTION_SIMPLE",
"transformBy" : {
"_id" : true
},
"inputStage" : {
"stage" : "FETCH",
"filter" : {
"$and" : [
{
"accountId" : {
"$eq" : ObjectId("62c56bbef2c6530d4a9b5976")
}
},
{
"dataHash" : {
"$eq" : "00002478a5c9594923833b3534b2d0b17bb7298a"
}
}
]
},
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"businessId" : 1.0,
"reason" : 1.0,
"deleted" : 1.0
},
"indexName" : "businessId_1_reason_1_deleted_1",
"isMultiKey" : false,
"multiKeyPaths" : {
"businessId" : [
],
"reason" : [
],
"deleted" : [
]
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2.0,
"direction" : "forward",
"indexBounds" : {
"businessId" : [
"[ObjectId('62c56bbdba7f1d001368f217'), ObjectId('62c56bbdba7f1d001368f217')]"
],
"reason" : [
"[MinKey, MaxKey]"
],
"deleted" : [
"[MinKey, true)",
"(true, MaxKey]"
]
}
}
}
},
"rejectedPlans" : [
{
"stage" : "PROJECTION_SIMPLE",
"transformBy" : {
"_id" : true
},
"inputStage" : {
"stage" : "FETCH",
"filter" : {
"$and" : [
{
"accountId" : {
"$eq" : ObjectId("62c56bbef2c6530d4a9b5976")
}
},
{
"dataHash" : {
"$eq" : "00002478a5c9594923833b3534b2d0b17bb7298a"
}
}
]
},
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"businessId" : 1.0,
"tags" : 1.0,
"deleted" : 1.0
},
"indexName" : "businessId_1_tags_1_deleted_1",
"isMultiKey" : true,
"multiKeyPaths" : {
"businessId" : [
],
"tags" : [
"tags"
],
"deleted" : [
]
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2.0,
"direction" : "forward",
"indexBounds" : {
"businessId" : [
"[ObjectId('62c56bbdba7f1d001368f217'), ObjectId('62c56bbdba7f1d001368f217')]"
],
"tags" : [
"[MinKey, MaxKey]"
],
"deleted" : [
"[MinKey, true)",
"(true, MaxKey]"
]
}
}
}
},
{
"stage" : "PROJECTION_SIMPLE",
"transformBy" : {
"_id" : true
},
"inputStage" : {
"stage" : "FETCH",
"filter" : {
"$and" : [
{
"accountId" : {
"$eq" : ObjectId("62c56bbef2c6530d4a9b5976")
}
},
{
"dataHash" : {
"$eq" : "00002478a5c9594923833b3534b2d0b17bb7298a"
}
}
]
},
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"businessId" : 1.0,
"documentId" : 1.0,
"status" : 1.0,
"deleted" : 1.0
},
"indexName" : "businessId_1_documentId_1_status_1_deleted_1",
"isMultiKey" : false,
"multiKeyPaths" : {
"businessId" : [
],
"documentId" : [
],
"status" : [
],
"deleted" : [
]
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2.0,
"direction" : "forward",
"indexBounds" : {
"businessId" : [
"[ObjectId('62c56bbdba7f1d001368f217'), ObjectId('62c56bbdba7f1d001368f217')]"
],
"documentId" : [
"[MinKey, MaxKey]"
],
"status" : [
"[MinKey, MaxKey]"
],
"deleted" : [
"[MinKey, true)",
"(true, MaxKey]"
]
}
}
}
},
{
"stage" : "PROJECTION_SIMPLE",
"transformBy" : {
"_id" : true
},
"inputStage" : {
"stage" : "FETCH",
"filter" : {
"$and" : [
{
"businessId" : {
"$eq" : ObjectId("62c56bbdba7f1d001368f217")
}
},
{
"dataHash" : {
"$eq" : "00002478a5c9594923833b3534b2d0b17bb7298a"
}
}
]
},
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"accountId" : 1.0,
"description" : 1.0,
"address.name" : 1.0,
"deleted" : 1.0
},
"indexName" : "accountId_1_description_1_address.name_1_deleted_1",
"isMultiKey" : false,
"multiKeyPaths" : {
"accountId" : [
],
"description" : [
],
"address.name" : [
],
"deleted" : [
]
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2.0,
"direction" : "forward",
"indexBounds" : {
"accountId" : [
"[ObjectId('62c56bbef2c6530d4a9b5976'), ObjectId('62c56bbef2c6530d4a9b5976')]"
],
"description" : [
"[MinKey, MaxKey]"
],
"address.name" : [
"[MinKey, MaxKey]"
],
"deleted" : [
"[MinKey, true)",
"(true, MaxKey]"
]
}
}
}
},
{
"stage" : "PROJECTION_SIMPLE",
"transformBy" : {
"_id" : true
},
"inputStage" : {
"stage" : "FETCH",
"filter" : {
"$and" : [
{
"businessId" : {
"$eq" : ObjectId("62c56bbdba7f1d001368f217")
}
},
{
"dataHash" : {
"$eq" : "00002478a5c9594923833b3534b2d0b17bb7298a"
}
}
]
},
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"accountId" : 1.0,
"timestamp" : 1.0,
"amount" : 1.0,
"type" : 1.0,
"deleted" : 1.0
},
"indexName" : "accountId_1_timestamp_1_amount_1_type_1_deleted_1",
"isMultiKey" : false,
"multiKeyPaths" : {
"accountId" : [
],
"timestamp" : [
],
"amount" : [
],
"type" : [
],
"deleted" : [
]
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2.0,
"direction" : "forward",
"indexBounds" : {
"accountId" : [
"[ObjectId('62c56bbef2c6530d4a9b5976'), ObjectId('62c56bbef2c6530d4a9b5976')]"
],
"timestamp" : [
"[MinKey, MaxKey]"
],
"amount" : [
"[MinKey, MaxKey]"
],
"type" : [
"[MinKey, MaxKey]"
],
"deleted" : [
"[MinKey, true)",
"(true, MaxKey]"
]
}
}
}
},
{
"stage" : "PROJECTION_SIMPLE",
"transformBy" : {
"_id" : true
},
"inputStage" : {
"stage" : "FETCH",
"filter" : {
"dataHash" : {
"$eq" : "00002478a5c9594923833b3534b2d0b17bb7298a"
}
},
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"businessId" : 1.0,
"accountId" : 1.0,
"deleted" : 1.0
},
"indexName" : "businessId_1_accountId_1_deleted_1",
"isMultiKey" : false,
"multiKeyPaths" : {
"businessId" : [
],
"accountId" : [
],
"deleted" : [
]
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2.0,
"direction" : "forward",
"indexBounds" : {
"businessId" : [
"[ObjectId('62c56bbdba7f1d001368f217'), ObjectId('62c56bbdba7f1d001368f217')]"
],
"accountId" : [
"[ObjectId('62c56bbef2c6530d4a9b5976'), ObjectId('62c56bbef2c6530d4a9b5976')]"
],
"deleted" : [
"[MinKey, true)",
"(true, MaxKey]"
]
}
}
}
},
{
"stage" : "PROJECTION_SIMPLE",
"transformBy" : {
"_id" : true
},
"inputStage" : {
"stage" : "FETCH",
"filter" : {
"dataHash" : {
"$eq" : "00002478a5c9594923833b3534b2d0b17bb7298a"
}
},
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"businessId" : 1.0,
"accountId" : 1.0,
"vendorId" : 1.0,
"category.code" : 1.0,
"deleted" : 1.0
},
"indexName" : "businessId_1_accountId_1_vendorId_1_category.code_1_deleted_1",
"isMultiKey" : false,
"multiKeyPaths" : {
"businessId" : [
],
"accountId" : [
],
"vendorId" : [
],
"category.code" : [
],
"deleted" : [
]
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2.0,
"direction" : "forward",
"indexBounds" : {
"businessId" : [
"[ObjectId('62c56bbdba7f1d001368f217'), ObjectId('62c56bbdba7f1d001368f217')]"
],
"accountId" : [
"[ObjectId('62c56bbef2c6530d4a9b5976'), ObjectId('62c56bbef2c6530d4a9b5976')]"
],
"vendorId" : [
"[MinKey, MaxKey]"
],
"category.code" : [
"[MinKey, MaxKey]"
],
"deleted" : [
"[MinKey, true)",
"(true, MaxKey]"
]
}
}
}
},
{
"stage" : "PROJECTION_SIMPLE",
"transformBy" : {
"_id" : true
},
"inputStage" : {
"stage" : "FETCH",
"filter" : {
"$and" : [
{
"accountId" : {
"$eq" : ObjectId("62c56bbef2c6530d4a9b5976")
}
},
{
"businessId" : {
"$eq" : ObjectId("62c56bbdba7f1d001368f217")
}
}
]
},
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"dataHash" : 1.0,
"deleted" : 1.0
},
"indexName" : "dataHash_1_deleted_1",
"isMultiKey" : false,
"multiKeyPaths" : {
"dataHash" : [
],
"deleted" : [
]
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2.0,
"direction" : "forward",
"indexBounds" : {
"dataHash" : [
"[\"00002478a5c9594923833b3534b2d0b17bb7298a\", \"00002478a5c9594923833b3534b2d0b17bb7298a\"]"
],
"deleted" : [
"[MinKey, true)",
"(true, MaxKey]"
]
}
}
}
},
{
"stage" : "PROJECTION_SIMPLE",
"transformBy" : {
"_id" : true
},
"inputStage" : {
"stage" : "FETCH",
"filter" : {
"$and" : [
{
"accountId" : {
"$eq" : ObjectId("62c56bbef2c6530d4a9b5976")
}
},
{
"dataHash" : {
"$eq" : "00002478a5c9594923833b3534b2d0b17bb7298a"
}
}
]
},
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"businessId" : 1.0,
"address.name" : 1.0,
"status" : 1.0,
"description" : 1.0,
"vendorId" : 1.0,
"deleted" : 1.0
},
"indexName" : "businessId_1_address.name_1_status_1_description_1_vendorId_1_deleted_1",
"isMultiKey" : false,
"multiKeyPaths" : {
"businessId" : [
],
"address.name" : [
],
"status" : [
],
"description" : [
],
"vendorId" : [
],
"deleted" : [
]
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2.0,
"direction" : "forward",
"indexBounds" : {
"businessId" : [
"[ObjectId('62c56bbdba7f1d001368f217'), ObjectId('62c56bbdba7f1d001368f217')]"
],
"address.name" : [
"[MinKey, MaxKey]"
],
"status" : [
"[MinKey, MaxKey]"
],
"description" : [
"[MinKey, MaxKey]"
],
"vendorId" : [
"[MinKey, MaxKey]"
],
"deleted" : [
"[MinKey, true)",
"(true, MaxKey]"
]
}
}
}
},
{
"stage" : "PROJECTION_SIMPLE",
"transformBy" : {
"_id" : true
},
"inputStage" : {
"stage" : "FETCH",
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"businessId" : 1.0,
"accountId" : 1.0,
"dataHash" : 1.0,
"deleted" : 1.0
},
"indexName" : "businessId_1_accountId_1_dataHash_1_deleted_1",
"isMultiKey" : false,
"multiKeyPaths" : {
"businessId" : [
],
"accountId" : [
],
"dataHash" : [
],
"deleted" : [
]
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2.0,
"direction" : "forward",
"indexBounds" : {
"businessId" : [
"[ObjectId('62c56bbdba7f1d001368f217'), ObjectId('62c56bbdba7f1d001368f217')]"
],
"accountId" : [
"[ObjectId('62c56bbef2c6530d4a9b5976'), ObjectId('62c56bbef2c6530d4a9b5976')]"
],
"dataHash" : [
"[\"00002478a5c9594923833b3534b2d0b17bb7298a\", \"00002478a5c9594923833b3534b2d0b17bb7298a\"]"
],
"deleted" : [
"[MinKey, true)",
"(true, MaxKey]"
]
}
}
}
}
]
},
"command" : {
"find" : "transactions",
"filter" : {
"businessId" : ObjectId("62c56bbdba7f1d001368f217"),
"accountId" : ObjectId("62c56bbef2c6530d4a9b5976"),
"dataHash" : "00002478a5c9594923833b3534b2d0b17bb7298a",
"deleted" : {
"$ne" : true
}
},
"projection" : {
"_id" : true
},
"$db" : "stage"
},
"serverInfo" : {
"host" : "cluster0-shard-00-01.u0b4v.mongodb.net",
"port" : 27017.0,
"version" : "5.0.12",
"gitVersion" : "79cfcdd83eb6f64e164a588d0daf9bb873328b45"
},
"serverParameters" : {
"internalQueryFacetBufferSizeBytes" : 104857600.0,
"internalQueryFacetMaxOutputDocSizeBytes" : 104857600.0,
"internalLookupStageIntermediateDocumentMaxSizeBytes" : 16793600.0,
"internalDocumentSourceGroupMaxMemoryBytes" : 104857600.0,
"internalQueryMaxBlockingSortMemoryUsageBytes" : 33554432.0,
"internalQueryProhibitBlockingMergeOnMongoS" : 0.0,
"internalQueryMaxAddToSetBytes" : 104857600.0,
"internalDocumentSourceSetWindowFieldsMaxMemoryBytes" : 104857600.0
},
"ok" : 1.0,
"$clusterTime" : {
"clusterTime" : Timestamp(1663107520, 28),
"signature" : {
"hash" : BinData(0, "E5Mndz8gTLw14zfsCVW0yZAa9hg="),
"keyId" : NumberLong(7083587916596772929)
}
},
"operationTime" : Timestamp(1663107520, 28)
}
Winning plan index is businessId_1_reason_1_deleted_1, so it uses only businessId for indexing. Of course I can use .hint(), but... Why it happens? I read the docs, but did not found answer. It should work
Accurately answering this question requires a higher verbosity explain output, namely "allPlansExecution". If you can provide that we can take a look and I'd be happy to amend my answer with any additional observations learned from it.
In the absence of such information, we can make the following observations:
As with any database system, the environmental inputs to the query planner are very important. Is the problem being experienced on an environment that has data representative of a production system? It is common for development environments that don't contain much data to select "unexpected" plans, but that is mostly a consequence of not having appropriate information to make a decision as opposed to a problem that will manifest in practice with real data.
This environment has quite a few indexes, many of which contain similar fields. In addition to the commonly recited considerations for indexes (such as incremental impact on write throughput and increased space usage), they also result in more complexity for the optimizer when planning queries. This may represent an opportunity to review and potentially consolidate some of your indexes.
If this query, specifically the projection to only retrieve the _id field, is what the application will be doing in practice, then consider appending the _id field to the end of the index. This should allow the database to skip retrieval of the full document via the FETCH just to obtain that single value. In other words, such an index would result in a Covered Query option for the optimizer to consider.

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 Query plan not using compound index

I am trying MongoDB with a dataset about the company profile margin for learning purpose. Here is the sample document
{
"parent_comp" : 1
"child_comp" : 101
"profit" : NumberLong(70320020)
}
I have created two indexes i.e one on child_comp field and the other one is a compound index with parent_comp, child_comp, and last_outage_timestamp.
For the below query, I executed the explain command to see the query plan.
MongoDB Enterprise > db.data.find({ "$and" : [{ "parent_comp" : 951, "child_comp" : 9351, "profit" : { "$gte" : { "$numberLong" : "500000000" } } }, { "profit" : { "$lte" : { "$numberLong" : "1000000000" } } }] }).sort({"profit" : 1}).limit(3).explain();
{
"queryPlanner" : {
"plannerVersion" : 1,
"namespace" : "test.data",
"indexFilterSet" : false,
"parsedQuery" : {
"$and" : [
{
"child_comp" : {
"$eq" : 9351
}
},
{
"parent_comp" : {
"$eq" : 951
}
},
{
"profit" : {
"$lte" : {
"$numberLong" : "1000000000"
}
}
},
{
"profit" : {
"$gte" : {
"$numberLong" : "500000000"
}
}
}
]
},
"queryHash" : "B570EF0C",
"planCacheKey" : "187EF74B",
"winningPlan" : {
"stage" : "LIMIT",
"limitAmount" : 3,
"inputStage" : {
"stage" : "FETCH",
"filter" : {
"$and" : [
{
"child_comp" : {
"$eq" : 9351
}
},
{
"parent_comp" : {
"$eq" : 951
}
}
]
},
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"profit" : 1
},
"indexName" : "profit_index",
"isMultiKey" : false,
"multiKeyPaths" : {
"profit" : [ ]
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "forward",
"indexBounds" : {
"profit" : [
"[{ $numberLong: \"500000000\" }, { $numberLong: \"1000000000\" }]"
]
}
}
}
},
"rejectedPlans" : [
{
"stage" : "SORT",
"sortPattern" : {
"profit" : 1
},
"limitAmount" : 3,
"inputStage" : {
"stage" : "SORT_KEY_GENERATOR",
"inputStage" : {
"stage" : "FETCH",
"filter" : {
"$and" : [
{
"parent_comp" : {
"$eq" : 951
}
},
{
"profit" : {
"$lte" : {
"$numberLong" : "1000000000"
}
}
},
{
"profit" : {
"$gte" : {
"$numberLong" : "500000000"
}
}
}
]
},
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"child_comp" : 1
},
"indexName" : "child_comp_index",
"isMultiKey" : false,
"multiKeyPaths" : {
"child_comp" : [ ]
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "forward",
"indexBounds" : {
"child_comp" : [
"[9351.0, 9351.0]"
]
}
}
}
}
},
{
"stage" : "LIMIT",
"limitAmount" : 3,
"inputStage" : {
"stage" : "FETCH",
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"parent_comp" : 1,
"child_comp" : 1,
"profit" : 1
},
"indexName" : "parent_comp_1_child_comp_1_profit_1",
"isMultiKey" : false,
"multiKeyPaths" : {
"parent_comp" : [ ],
"child_comp" : [ ],
"profit" : [ ]
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "forward",
"indexBounds" : {
"parent_comp" : [
"[951.0, 951.0]"
],
"child_comp" : [
"[9351.0, 9351.0]"
],
"profit" : [
"[{ $numberLong: \"500000000\" }, { $numberLong: \"1000000000\" }]"
]
}
}
}
}
]
},
"serverInfo" : {
"host" : "localhost",
"port" : 27017,
"version" : "4.2.8",
"gitVersion" : "43d25888249164d76d5e04dd6cf38f6111e21f5f"
},
"ok" : 1
}
As you can see winning plan used single index instead of compound index. So could you please let me know why compound index was not used.
Your query is sorting on profit, and the compound index does not include the field you are sorting on hence using the compound index would necessitate an additional sort stage.
The trade-offs and reasoning is further explained in the docs.
See also https://www.alexbevi.com/blog/2020/05/16/optimizing-mongodb-compound-indexes-the-equality-sort-range-esr-rule/.

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.

MongoDB $or query against itself much faster

I have a mongo instance with 16m documents in a collection. I was writing a query to search for one of the (indexed) fields and I'm getting some weird results, which I cannot explain.
If I execute a query directly like:
find({ "$and" : [ { "ipAddr" : { "$regex" : "^01:172"}} , { "active" : true}]}).limit(100).sort({ "_id" : 1})
or even adding a pointless $or to the query:
find({ "$and" : [ { "$or" : [ { "ipAddr" : { "$regex" : "^01:172"}}]} , { "active" : true}]}).limit(100).sort({ "_id" : 1})
It returns
Fetched 3 record(s) in 71673ms
However, if I use an $or against itself like:
find({ "$and" : [ { "$or" : [ { "ipAddr" : { "$regex" : "^01:172"}} , { "ipAddr" : { "$regex" : "^01:172"}}]} , { "active" : true}]}).limit(100).sort({ "_id" : 1})
It returns:
Fetched 3 record(s) in 4ms
So a big performance difference. From inspecting the explain() on the queries, I could not determine why such a large performance difference exists. Can anyone shed light on what I'm missing or what mongo is doing differently between these?
Explain() on single $or which takes >60000ms
find({ "$and" : [ { "$or" : [ { "ipAddr" : { "$regex" : "^01:172"}}]} , { "active" : true}]}).limit(100).sort({ "_id" : 1}).explain()
{
"queryPlanner" : {
"plannerVersion" : 1,
"namespace" : "CLS-TEST.Leases",
"indexFilterSet" : false,
"parsedQuery" : {
"$and" : [
{
"active" : {
"$eq" : true
}
},
{
"ipAddr" : /^01:172/
}
]
},
"winningPlan" : {
"stage" : "SORT",
"sortPattern" : {
"_id" : 1
},
"limitAmount" : 100,
"inputStage" : {
"stage" : "SORT_KEY_GENERATOR",
"inputStage" : {
"stage" : "FETCH",
"filter" : {
"active" : {
"$eq" : true
}
},
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"ipAddr" : 1
},
"indexName" : "ipAddr_1",
"isMultiKey" : false,
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 1,
"direction" : "forward",
"indexBounds" : {
"ipAddr" : [
"[\"01:172\", \"01:173\")",
"[/^01:172/, /^01:172/]"
]
}
}
}
}
},
"rejectedPlans" : [
{
"stage" : "SORT",
"sortPattern" : {
"_id" : 1
},
"limitAmount" : 100,
"inputStage" : {
"stage" : "SORT_KEY_GENERATOR",
"inputStage" : {
"stage" : "FETCH",
"filter" : {
"ipAddr" : /^01:172/
},
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"active" : 1,
"sessionId" : 1,
"updateTime" : 1
},
"indexName" : "active_1_sessionId_1_updateTime_1",
"isMultiKey" : false,
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 1,
"direction" : "forward",
"indexBounds" : {
"active" : [
"[true, true]"
],
"sessionId" : [
"[MinKey, MaxKey]"
],
"updateTime" : [
"[MinKey, MaxKey]"
]
}
}
}
}
},
{
"stage" : "SORT",
"sortPattern" : {
"_id" : 1
},
"limitAmount" : 100,
"inputStage" : {
"stage" : "SORT_KEY_GENERATOR",
"inputStage" : {
"stage" : "FETCH",
"filter" : {
"ipAddr" : /^01:172/
},
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"active" : 1,
"clientId" : 1,
"startTime" : -1,
"_id" : -1
},
"indexName" : "active_1_clientId_1_startTime_-1__id_-1",
"isMultiKey" : false,
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 1,
"direction" : "forward",
"indexBounds" : {
"active" : [
"[true, true]"
],
"clientId" : [
"[MinKey, MaxKey]"
],
"startTime" : [
"[MaxKey, MinKey]"
],
"_id" : [
"[MaxKey, MinKey]"
]
}
}
}
}
},
{
"stage" : "SORT",
"sortPattern" : {
"_id" : 1
},
"limitAmount" : 100,
"inputStage" : {
"stage" : "SORT_KEY_GENERATOR",
"inputStage" : {
"stage" : "FETCH",
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"active" : 1,
"ipAddr" : 1,
"startTime" : -1,
"_id" : -1
},
"indexName" : "active_1_ipAddr_1_startTime_-1__id_-1",
"isMultiKey" : false,
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 1,
"direction" : "forward",
"indexBounds" : {
"active" : [
"[true, true]"
],
"ipAddr" : [
"[\"01:172\", \"01:173\")",
"[/^01:172/, /^01:172/]"
],
"startTime" : [
"[MaxKey, MinKey]"
],
"_id" : [
"[MaxKey, MinKey]"
]
}
}
}
}
},
{
"stage" : "SORT",
"sortPattern" : {
"_id" : 1
},
"limitAmount" : 100,
"inputStage" : {
"stage" : "SORT_KEY_GENERATOR",
"inputStage" : {
"stage" : "FETCH",
"filter" : {
"ipAddr" : /^01:172/
},
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"active" : 1,
"macAddress" : 1,
"startTime" : -1,
"_id" : -1
},
"indexName" : "active_1_macAddress_1_startTime_-1__id_-1",
"isMultiKey" : false,
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 1,
"direction" : "forward",
"indexBounds" : {
"active" : [
"[true, true]"
],
"macAddress" : [
"[MinKey, MaxKey]"
],
"startTime" : [
"[MaxKey, MinKey]"
],
"_id" : [
"[MaxKey, MinKey]"
]
}
}
}
}
},
{
"stage" : "SORT",
"sortPattern" : {
"_id" : 1
},
"limitAmount" : 100,
"inputStage" : {
"stage" : "SORT_KEY_GENERATOR",
"inputStage" : {
"stage" : "FETCH",
"filter" : {
"ipAddr" : /^01:172/
},
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"active" : 1,
"remoteId" : 1,
"startTime" : -1,
"_id" : -1
},
"indexName" : "active_1_remoteId_1_startTime_-1__id_-1",
"isMultiKey" : false,
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 1,
"direction" : "forward",
"indexBounds" : {
"active" : [
"[true, true]"
],
"remoteId" : [
"[MinKey, MaxKey]"
],
"startTime" : [
"[MaxKey, MinKey]"
],
"_id" : [
"[MaxKey, MinKey]"
]
}
}
}
}
},
{
"stage" : "LIMIT",
"limitAmount" : 100,
"inputStage" : {
"stage" : "FETCH",
"filter" : {
"$and" : [
{
"active" : {
"$eq" : true
}
},
{
"ipAddr" : /^01:172/
}
]
},
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"_id" : 1
},
"indexName" : "_id_",
"isMultiKey" : false,
"isUnique" : true,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 1,
"direction" : "forward",
"indexBounds" : {
"_id" : [
"[MinKey, MaxKey]"
]
}
}
}
}
]
},
"serverInfo" : {
"host" : "",
"port" : 27017,
"version" : "3.2.3",
"gitVersion" : "b326ba837cf6f49d65c2f85e1b70f6f31ece7937"
},
"ok" : 1
}
Explain() on $or against itself which takes <50ms
find({ "$and" : [ { "$or" : [ { "ipAddr" : { "$regex" : "^01:172"}} , { "ipAddr" : { "$regex" : "^01:172"}}]} , { "active" : true}]}).limit(100).sort({ "_id" : 1}).explain()
{
"queryPlanner" : {
"plannerVersion" : 1,
"namespace" : "CLS-TEST.Leases",
"indexFilterSet" : false,
"parsedQuery" : {
"$and" : [
{
"$or" : [
{
"ipAddr" : /^01:172/
},
{
"ipAddr" : /^01:172/
}
]
},
{
"active" : {
"$eq" : true
}
}
]
},
"winningPlan" : {
"stage" : "SORT",
"sortPattern" : {
"_id" : 1
},
"limitAmount" : 100,
"inputStage" : {
"stage" : "SORT_KEY_GENERATOR",
"inputStage" : {
"stage" : "FETCH",
"filter" : {
"active" : {
"$eq" : true
}
},
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"ipAddr" : 1
},
"indexName" : "ipAddr_1",
"isMultiKey" : false,
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 1,
"direction" : "forward",
"indexBounds" : {
"ipAddr" : [
"[\"01:172\", \"01:173\")",
"[/^01:172/, /^01:172/]"
]
}
}
}
}
},
"rejectedPlans" : [
{
"stage" : "SORT",
"sortPattern" : {
"_id" : 1
},
"limitAmount" : 100,
"inputStage" : {
"stage" : "SORT_KEY_GENERATOR",
"inputStage" : {
"stage" : "FETCH",
"filter" : {
"$or" : [
{
"ipAddr" : /^01:172/
},
{
"ipAddr" : /^01:172/
}
]
},
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"active" : 1,
"sessionId" : 1,
"updateTime" : 1
},
"indexName" : "active_1_sessionId_1_updateTime_1",
"isMultiKey" : false,
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 1,
"direction" : "forward",
"indexBounds" : {
"active" : [
"[true, true]"
],
"sessionId" : [
"[MinKey, MaxKey]"
],
"updateTime" : [
"[MinKey, MaxKey]"
]
}
}
}
}
},
{
"stage" : "SORT",
"sortPattern" : {
"_id" : 1
},
"limitAmount" : 100,
"inputStage" : {
"stage" : "SORT_KEY_GENERATOR",
"inputStage" : {
"stage" : "FETCH",
"filter" : {
"$or" : [
{
"ipAddr" : /^01:172/
},
{
"ipAddr" : /^01:172/
}
]
},
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"active" : 1,
"clientId" : 1,
"startTime" : -1,
"_id" : -1
},
"indexName" : "active_1_clientId_1_startTime_-1__id_-1",
"isMultiKey" : false,
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 1,
"direction" : "forward",
"indexBounds" : {
"active" : [
"[true, true]"
],
"clientId" : [
"[MinKey, MaxKey]"
],
"startTime" : [
"[MaxKey, MinKey]"
],
"_id" : [
"[MaxKey, MinKey]"
]
}
}
}
}
},
{
"stage" : "SORT",
"sortPattern" : {
"_id" : 1
},
"limitAmount" : 100,
"inputStage" : {
"stage" : "SORT_KEY_GENERATOR",
"inputStage" : {
"stage" : "FETCH",
"filter" : {
"$or" : [
{
"ipAddr" : /^01:172/
},
{
"ipAddr" : /^01:172/
}
]
},
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"active" : 1,
"ipAddr" : 1,
"startTime" : -1,
"_id" : -1
},
"indexName" : "active_1_ipAddr_1_startTime_-1__id_-1",
"isMultiKey" : false,
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 1,
"direction" : "forward",
"indexBounds" : {
"active" : [
"[true, true]"
],
"ipAddr" : [
"[MinKey, MaxKey]"
],
"startTime" : [
"[MaxKey, MinKey]"
],
"_id" : [
"[MaxKey, MinKey]"
]
}
}
}
}
},
{
"stage" : "SORT",
"sortPattern" : {
"_id" : 1
},
"limitAmount" : 100,
"inputStage" : {
"stage" : "SORT_KEY_GENERATOR",
"inputStage" : {
"stage" : "FETCH",
"filter" : {
"$or" : [
{
"ipAddr" : /^01:172/
},
{
"ipAddr" : /^01:172/
}
]
},
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"active" : 1,
"macAddress" : 1,
"startTime" : -1,
"_id" : -1
},
"indexName" : "active_1_macAddress_1_startTime_-1__id_-1",
"isMultiKey" : false,
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 1,
"direction" : "forward",
"indexBounds" : {
"active" : [
"[true, true]"
],
"macAddress" : [
"[MinKey, MaxKey]"
],
"startTime" : [
"[MaxKey, MinKey]"
],
"_id" : [
"[MaxKey, MinKey]"
]
}
}
}
}
},
{
"stage" : "SORT",
"sortPattern" : {
"_id" : 1
},
"limitAmount" : 100,
"inputStage" : {
"stage" : "SORT_KEY_GENERATOR",
"inputStage" : {
"stage" : "FETCH",
"filter" : {
"$or" : [
{
"ipAddr" : /^01:172/
},
{
"ipAddr" : /^01:172/
}
]
},
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"active" : 1,
"remoteId" : 1,
"startTime" : -1,
"_id" : -1
},
"indexName" : "active_1_remoteId_1_startTime_-1__id_-1",
"isMultiKey" : false,
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 1,
"direction" : "forward",
"indexBounds" : {
"active" : [
"[true, true]"
],
"remoteId" : [
"[MinKey, MaxKey]"
],
"startTime" : [
"[MaxKey, MinKey]"
],
"_id" : [
"[MaxKey, MinKey]"
]
}
}
}
}
},
{
"stage" : "LIMIT",
"limitAmount" : 100,
"inputStage" : {
"stage" : "FETCH",
"filter" : {
"$and" : [
{
"$or" : [
{
"ipAddr" : /^01:172/
},
{
"ipAddr" : /^01:172/
}
]
},
{
"active" : {
"$eq" : true
}
}
]
},
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"_id" : 1
},
"indexName" : "_id_",
"isMultiKey" : false,
"isUnique" : true,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 1,
"direction" : "forward",
"indexBounds" : {
"_id" : [
"[MinKey, MaxKey]"
]
}
}
}
}
]
},
"serverInfo" : {
"host" : "",
"port" : 27017,
"version" : "3.2.3",
"gitVersion" : "b326ba837cf6f49d65c2f85e1b70f6f31ece7937"
},
"ok" : 1
}
You might note that none of the index selections include the combination of "active" and "ipAddr", which would be the useful index to define here.
In short, the "slower" query is using just the index for "ipAddr" and therefore it's taking more work to then "filter" out the { "active": true } entries.
Clearly when the other index selection(s) are using the "active" key with those bounds there are less results being passed to the subsequent filter on the regex pattern. You seem to have quite a few indexes here, and none of them are really optimal for the query.
I'll give you props for at least running the "explain" output on either query, but if you look closely then you should see that the "slow" query "incorrectly" chooses the "ipAddr" index thinking it to be optimal. It might not be, but it's a reasonable assumption for the optimiser to make considering an "anchored" regex in use.
The $or forces "index intersection", and it's smart enough to not do that when there is only "one" argument in the $or. "Two" arguments makes that happen, and the optimiser takes another "guess" by looking for an index preceeding with your other query condition ( the "active" value ).
That makes sense, since this is now running "two" queries from which it wil "intersect" the results, and therefore any conditions outside of the $or statement would be the logical choice to optimally choose an index for.
Since the returned results from these is likely smaller, it's therefore faster to "filter" out the regex match than to look at all the regex results and filter out the "active" values.
Therefore the "best" index to define for this query would be:
.createIndex({ "active": 1, "ipAddr": 1 })
And then the results from either query would be consistent, providing of course that the optimiser does not get confused by other indexes present and chooses that one. To force the index selection, use .hint()