MongoDB
 sql >> Base de données >  >> NoSQL >> MongoDB

Comment fonctionne le tri avec les requêtes `$or` et `$in` dans MongoDB ?

Remarque : Cette réponse est basée sur MongoDB 3.2.4.

Il est intéressant de découvrir l'utilisation de explain() dans MongoDB. Le explain() sortie d'une requête (par exemple db.collection.explain().find(...) ) vous permet de vérifier quel index est utilisé dans une requête et d'utiliser db.collection.explain('executionStats') vous montrera également si la requête réussit ou échoue en raison de SORT en mémoire limitation.

en $

Un $in La requête peut être considérée comme une série de requêtes d'égalité. Par exemple, {a: {$in: [1,3,5]}} pourrait être considéré comme {a:1}, {a:3}, {a:5} . MongoDB triera les $in tableau avant de poursuivre la requête, de sorte que {$in: [3,5,1]} n'est pas différent de {$in: [1,3,5]} .

Supposons que la collection a un index de

{a:1, b:1}
  • Trier par a

      db.coll.find({a: {$in: [1,3,5]}}).sort({a:1})
    

    MongoDB pourra utiliser le {a:1,b:1} index, puisque cette requête peut être considérée comme une union de {a:1}, {a:3}, {a:5} requêtes. Trier par {a:1} permet l'utilisation de préfixe d'index , MongoDB n'a donc pas besoin d'effectuer un tri en mémoire.

    La même situation s'applique également à la requête :

      db.coll.find({a: {$in: [1,3,5]} ,b:{$gte:1, $lt:2}}).sort({a:1})
    

    depuis sort({a:1}) utilise également le préfixe d'index (a dans ce cas), un SORT en mémoire étape n'est donc pas nécessaire.

  • Trier par b

    C'est un cas plus intéressant par rapport au tri par a . Par exemple :

      db.coll.find({a: {$in: [1,3,5]}}).sort({b:1})
    

    Le explain() la sortie de cette requête aura une étape appelée SORT_MERGE . Rappelez-vous que le find() partie de la requête peut être considérée comme {a:1}, {a:3}, {a:5} .

    La requête db.coll.find({a:1}).sort({b:1}) n'a pas besoin d'avoir un SORT en mémoire étape en raison de la nature du {a:1,b:1} index :c'est-à-dire que MongoDB peut simplement parcourir l'index (trié) et renvoyer les documents triés par b après avoir satisfait le paramètre d'égalité sur a . Par exemple, pour chaque a , il y a beaucoup de b qui sont déjà triés par b grâce à l'index.

    Utiliser $in , la requête globale peut être considérée comme :

    • db.coll.find({a:1}).sort({b:1})
    • db.coll.find({a:3}).sort({b:1})
    • db.coll.find({a:5}).sort({b:1})
    • Prenez les résultats de requête individuels ci-dessus et effectuez une fusion en utilisant la valeur de b . La requête n'a pas besoin d'étape de tri en mémoire car les résultats de la requête individuelle sont déjà triés par b . MongoDB a juste besoin de fusionner les résultats de la sous-requête (déjà triés) en un seul résultat.

    De même, la requête

      db.coll.find({a: {$in: [1,3,5]} ,b:{$gte:1, $lt:2}}).sort({b:1})
    

    utilise également un SORT_MERGE stade et est très similaire à la requête ci-dessus. La différence est que les requêtes individuelles produisent des documents basés sur une plage de b (au lieu de tous b ) pour chaque a (qui seront triés par b en raison de l'indice {a:1,b:1} ). Par conséquent, la requête n'a pas besoin d'une étape de tri en mémoire.

$ou

Pour un $or requête pour utiliser un index, chaque clause dans le $or l'expression doit être associée à un index . Si cette exigence est satisfaite, il est possible que la requête emploie un SORT_MERGE étape comme un $in requête. Par exemple :

db.coll.explain().find({$or:[{a:1},{a:3},{a:5}]}).sort({b:1})

aura un plan de requête, une utilisation d'index et SORT_MERGE presque identiques étape comme dans le $in exemple ci-dessus. Essentiellement, la requête peut être considérée comme :

  • db.coll.find({a:1}).sort({b:1})
  • db.coll.find({a:3}).sort({b:1})
  • db.coll.find({a:5}).sort({b:1})
  • Prenez les résultats de requête individuels ci-dessus et effectuez une fusion en utilisant la valeur de b .

tout comme le $in exemple avant.

Cependant, cette requête :

db.coll.explain().find({$or:[{a:1},{b:1}]}).sort({b:1})

ne peut utiliser aucun index (puisque nous n'avons pas le {b:1} indice). Cette requête se traduira par une analyse de collection, et par conséquent aura une étape de tri en mémoire puisqu'aucun index n'est utilisé.

Si toutefois nous créons l'index {b:1} , la requête se déroulera comme :

  • db.coll.find({a:1}).sort({b:1})
  • db.coll.find({b:1}).sort({b:1})
  • Prenez les résultats de requête individuels ci-dessus et effectuez une fusion en utilisant la valeur de b (qui est déjà trié aux deux sous-requêtes, en raison des index {a:1,b:1} et {b:1} ).

et MongoDB combinera les résultats de {a:1} et {b:1} requêtes et effectuer une fusion sur les résultats. Le processus de fusion est un temps linéaire, par ex. O(n) .

En conclusion, dans un $or requête, chaque terme doit avoir un index, y compris le sort() organiser. Sinon, MongoDB devra effectuer un tri en mémoire.