Nota: Questa risposta si basa su MongoDB 3.2.4.
Vale la pena scoprire l'uso di explain()
in MongoDB. Il explain()
output di una query (ad es. db.collection.explain().find(...)
) consente di verificare quale indice viene utilizzato in una query e di utilizzare db.collection.explain('executionStats')
ti mostrerà anche se la query ha esito positivo o negativo a causa di SORT
in memoria limitazione.
$ in
Un $in
query può essere considerata come una serie di query di uguaglianza. Ad esempio, {a: {$in: [1,3,5]}}
potrebbe essere pensato come {a:1}, {a:3}, {a:5}
. MongoDB ordinerà $in
array prima di procedere con la query, in modo che {$in: [3,5,1]}
non è diverso da {$in: [1,3,5]}
.
Supponiamo che la raccolta abbia un indice di
{a:1, b:1}
-
Ordinamento per
a
db.coll.find({a: {$in: [1,3,5]}}).sort({a:1})
MongoDB sarà in grado di utilizzare il
{a:1,b:1}
index, poiché questa query può essere considerata come un'unione di{a:1}, {a:3}, {a:5}
interrogazioni. Ordinamento per{a:1}
consente l'uso di prefisso indice , quindi MongoDB non ha bisogno di eseguire un ordinamento in memoria.La stessa situazione vale anche per la query:
db.coll.find({a: {$in: [1,3,5]} ,b:{$gte:1, $lt:2}}).sort({a:1})
poiché
sort({a:1})
usa anche il prefisso dell'indice (a
in questo caso), unSORT
in memoria la fase non è quindi richiesta. -
Ordinamento per
b
Questo è un caso più interessante rispetto all'ordinamento per
a
. Ad esempio:db.coll.find({a: {$in: [1,3,5]}}).sort({b:1})
Il
explain()
l'output di questa query avrà una fase denominataSORT_MERGE
. Ricorda che ilfind()
parte della query può essere considerata come{a:1}, {a:3}, {a:5}
.La query
db.coll.find({a:1}).sort({b:1})
non è necessario avere unSORT
in memoria fase a causa della natura del{a:1,b:1}
index:ovvero, MongoDB può semplicemente percorrere l'indice (ordinato) e restituire documenti ordinati perb
dopo aver soddisfatto il parametro di uguaglianza sua
. Ad esempio, per ognia
, ci sono moltib
che sono già ordinati perb
a causa dell'indice.Usando
$in
, la query complessiva può essere considerata come:db.coll.find({a:1}).sort({b:1})
db.coll.find({a:3}).sort({b:1})
db.coll.find({a:5}).sort({b:1})
- Prendi i risultati della singola query sopra ed esegui un'unione utilizzando il valore di
b
. La query non necessita di una fase di ordinamento in memoria perché i risultati delle singole query sono già ordinati perb
. MongoDB ha solo bisogno di unire i risultati della sottoquery (già ordinati) in un unico risultato.
Allo stesso modo, la query
db.coll.find({a: {$in: [1,3,5]} ,b:{$gte:1, $lt:2}}).sort({b:1})
utilizza anche un
SORT_MERGE
stage ed è molto simile alla query precedente. La differenza è che le singole query generano documenti in base a un intervallo dib
(anziché ognib
) per ognia
(che sarà ordinato perb
a causa dell'indice{a:1,b:1}
). Pertanto, la query non necessita di una fase di ordinamento in memoria.
$o
Per un $or
query per utilizzare un indice, ogni clausola nel $or
l'espressione deve avere un indice associato
. Se questo requisito è soddisfatto, è possibile che la query utilizzi un SORT_MERGE
stage proprio come un $in
interrogazione. Ad esempio:
db.coll.explain().find({$or:[{a:1},{a:3},{a:5}]}).sort({b:1})
avrà un piano di query, un utilizzo dell'indice e SORT_MERGE
quasi identici come in $in
esempio sopra. In sostanza, la query può essere pensata come:
db.coll.find({a:1}).sort({b:1})
db.coll.find({a:3}).sort({b:1})
db.coll.find({a:5}).sort({b:1})
- Prendi i risultati della singola query sopra ed esegui un'unione utilizzando il valore di
b
.
proprio come il $in
esempio prima.
Tuttavia, questa domanda:
db.coll.explain().find({$or:[{a:1},{b:1}]}).sort({b:1})
non è possibile utilizzare alcun indice (dal momento che non abbiamo il {b:1}
indice). Questa query risulterà in una scansione della raccolta e, di conseguenza, avrà una fase di ordinamento in memoria poiché non viene utilizzato alcun indice.
Se, invece, creiamo l'indice {b:1}
, la query procederà come:
db.coll.find({a:1}).sort({b:1})
db.coll.find({b:1}).sort({b:1})
- Prendi i risultati della singola query sopra ed esegui un'unione utilizzando il valore di
b
(che è già ordinato in entrambe le sottoquery, a causa degli indici{a:1,b:1}
e{b:1}
).
e MongoDB combineranno i risultati di {a:1}
e {b:1}
query ed eseguire un'unione sui risultati. Il processo di fusione è tempo lineare, ad es. O(n)
.
In conclusione, in un $or
query, ogni termine deve avere un indice, incluso sort()
palcoscenico. In caso contrario, MongoDB dovrà eseguire un ordinamento in memoria.