- Make a collection with the following indexes:
- { status: 1, ignoreReason: 1, arr1: 1 }
- { status: 1, ignoreReason: 1, arr2: 1 }
- { status: 1, code: 1, ignoreReason: 1 }
- { status: 1, ignoreReason: 1, unrelatedField: 1 }
- Run the following queries:
{
$or: [
{
$or: [\{ arr1: [] }
, { arr2: [] }],
status:
{
$in: ["STATUS_1", "STATUS_2", "STATUS_3"],
}
,
},
{
status:
{
$in: ["STATUS_4", "STATUS_5", "STATUS_6"],
}
,
},
{
code: \{ $ne: null }
,
status: null,
},
],
ignoreReason: null,
}
{
$or: [
{
$or: [\{ arr1: [] }
, { arr2: [] }],
status:
{
$in: ["STATUS_1", "STATUS_2", "STATUS_3"],
}
,
ignoreReason: null,
},
{
status:
{
$in: ["STATUS_4", "STATUS_5", "STATUS_6"],
}
,
ignoreReason: null,
},
{
code: \{ $ne: null }
,
status: null,
ignoreReason: null,
},
],
}
When running the second query my database takes 88ms, examines 16 documents, and returns 14, while using the following plan:
"winningPlan": {
"isCached": false,
"stage": "SUBPLAN",
"inputStage":
{
"stage": "PROJECTION_SIMPLE",
"transformBy": \{ "_id": 1 }
,
"inputStage": {
"stage": "FETCH",
"inputStage": {
"stage": "OR",
"inputStages": [
{
"stage": "IXSCAN",
"keyPattern":
{
"status": 1,
"ignoreReason": 1,
"arr1": 1
}
,
"indexName": "status_1_ignoreReason_1_arr1_1",
"isMultiKey": true,
"multiKeyPaths":
{
"status": [],
"ignoreReason": [],
"arr1": [
"arr1"
]
}
,
"isUnique": false,
"isSparse": false,
"isPartial": false,
"indexVersion": 2,
"direction": "forward",
"indexBounds":
{
"status": [
"[\"STATUS_4\", \"STATUS_4\"]",
"[\"STATUS_5\", \"STATUS_5\"]",
"[\"STATUS_6\", \"STATUS_6\"]"
],
"ignoreReason": [
"[null, null]"
],
"arr1": [
"[MinKey, MaxKey]"
]
}
},
{
"stage": "OR",
"inputStages": [
{
"stage": "FETCH",
"filter": {
"arr1":
{
"$eq": []
}
},
"inputStage": {
"stage": "IXSCAN",
"keyPattern":
{
"status": 1,
"ignoreReason": 1,
"arr1": 1
}
,
"indexName": "status_1_ignoreReason_1_arr1_1",
"isMultiKey": true,
"multiKeyPaths":
{
"status": [],
"ignoreReason": [],
"arr1": [
"arr1"
]
}
,
"isUnique": false,
"isSparse": false,
"isPartial": false,
"indexVersion": 2,
"direction": "forward",
"indexBounds":
{
"status": [
"[\"STATUS_1\", \"STATUS_1\"]",
"[\"STATUS_2\", \"STATUS_2\"]",
"[\"STATUS_3\", \"STATUS_3\"]"
],
"ignoreReason": [
"[null, null]"
],
"arr1": [
"[undefined, undefined]",
"[[], []]"
]
}
}
},
{
"stage": "FETCH",
"filter":
{
"arr2": \{ "$eq": [] }
},
"inputStage": {
"stage": "IXSCAN",
"keyPattern":
{
"status": 1,
"ignoreReason": 1,
"arr2": 1
}
,
"indexName": "status_1_ignoreReason_1_arr2_1",
"isMultiKey": true,
"multiKeyPaths":
{
"status": [],
"ignoreReason": [],
"arr2": ["arr2"]
}
,
"isUnique": false,
"isSparse": false,
"isPartial": false,
"indexVersion": 2,
"direction": "forward",
"indexBounds":
{
"status": [
"[\"STATUS_1\", \"STATUS_1\"]",
"[\"STATUS_2\", \"STATUS_2\"]",
"[\"STATUS_3\", \"STATUS_3\"]"
],
"ignoreReason": [
"[null, null]"
],
"arr2": [
"[undefined, undefined]",
"[[], []]"
]
}
}
}
]
},
{
"stage": "IXSCAN",
"keyPattern":
{
"status": 1,
"bookingId": 1,
"ignoreReason": 1
}
,
"indexName": "status_1_bookingId_1_ignoreReason_1",
"isMultiKey": false,
"multiKeyPaths":
{
"status": [],
"bookingId": [],
"ignoreReason": []
}
,
"isUnique": false,
"isSparse": false,
"isPartial": false,
"indexVersion": 2,
"direction": "forward",
"indexBounds":
{
"status": [
"[null, null]"
],
"bookingId": [
"[MinKey, null)",
"(null, MaxKey]"
],
"ignoreReason": [
"[null, null]"
]
}
}
]
}
}
}
},
But when using the version with ignoreReason: null separate, it uses the much less efficient:
"winningPlan":
{
"isCached": false,
"stage": "PROJECTION_SIMPLE",
"transformBy": \{ "_id": 1 }
,
"inputStage": {
"stage": "FETCH",
"filter":
{
"ignoreReason": \{ "$eq": null }
},
"inputStage": {
"stage": "OR",
"inputStages": [
{
"stage": "FETCH",
"filter": {
"$or": [
{
"arr1":
{
"$eq": []
}
},
{ "arr2":
{ "$eq": [] }
}
]
},
"inputStage": {
"stage": "IXSCAN",
"keyPattern":
{
"status": 1,
"ignoreReason": 1,
"unrelated": 1
}
,
"indexName": "status_1_ignoreReason_1_unrelated_1",
"isMultiKey": false,
"multiKeyPaths":
{
"status": [],
"ignoreReason": [],
"unrelated": []
}
,
"isUnique": false,
"isSparse": false,
"isPartial": false,
"indexVersion": 2,
"direction": "forward",
"indexBounds":
{
"status": [
"[\"STATUS_1\", \"STATUS_1\"]",
"[\"STATUS_2\", \"STATUS_2\"]",
"[\"STATUS_3\", \"STATUS_3\"]"
],
"ignoreReason": [
"[null, null]"
],
"unrelated": [
"[MinKey, MaxKey]"
]
}
}
},
{
"stage": "IXSCAN",
"keyPattern":
{
"status": 1,
"code": 1,
"ignoreReason": 1
}
,
"indexName": "status_1_code_1_ignoreReason_1",
"isMultiKey": false,
"multiKeyPaths":
{
"status": [],
"code": [],
"ignoreReason": []
}
,
"isUnique": false,
"isSparse": false,
"isPartial": false,
"indexVersion": 2,
"direction": "forward",
"indexBounds":
{
"status": ["[null, null]"],
"code": [
"[MinKey, null)",
"(null, MaxKey]"
],
"ignoreReason": [
"[null, null]"
]
}
},
{
"stage": "IXSCAN",
"keyPattern":
{
"status": 1
}
,
"indexName": "status_1",
"isMultiKey": false,
"multiKeyPaths":
{
"status": []
}
,
"isUnique": false,
"isSparse": false,
"isPartial": false,
"indexVersion": 2,
"direction": "forward",
"indexBounds":
{
"status": [
"[\"STATUS_4\", \"STATUS_4\"]",
"[\"STATUS_5\", \"STATUS_5\"]",
"[\"STATUS_6\", \"STATUS_6\"]"
]
}
}
]
}
}
},
Which ends up scanning 71k documents to still only return the same 14.
Note: the above queries and winning plans are the actual ones used and reported in our database, but I have redacted the actual field names and values used. If those details would be relevant I am happy to talk to an engineer in a non-public forum.