MongoDB
 sql >> Database >  >> NoSQL >> MongoDB

Come funziona l'ordinamento con le query `$or` e `$in` in MongoDB?

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), un SORT 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 denominata SORT_MERGE . Ricorda che il find() 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 un SORT 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 per b dopo aver soddisfatto il parametro di uguaglianza su a . Ad esempio, per ogni a , ci sono molti b che sono già ordinati per b 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 per b . 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 di b (anziché ogni b ) per ogni a (che sarà ordinato per b 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.