registro - mongodb modificar documento
¿Cómo funciona la clasificación con las consultas `$ or` y` $ in` en MongoDB? (1)
Esta es una continuación de esta pregunta : mira eso para el contexto.
Esta pregunta se refiere a un par de casos especiales de la pregunta vinculada, es decir, cómo funciona la ordenación en MongoDB al usar $in
o $or
operadores, y cómo garantizar el uso de un índice para clasificar en lugar de ordenar en memoria.
$ en:
Por ejemplo, supongamos que tenemos una colección donde la estructura del documento es
{a: XXX, b: XXX}
... y tenemos un índice compuesto en b
en ese orden y queremos ejecutar la consulta
{a: {$in: [4, 6, 2, 1, 3, 10]}, b: {$gt: 1, $lt: 6}}
¿Cómo procedería el género si estuviera en a
o b
? $in
es una especie de operador de igualdad, pero me parece que un tipo en b
con un índice es aún imposible. Una ordenación en el uso de un índice solo es posible si el conjunto $in
value se ordena primero, creo, pero no sé si MongoDB lo hace.
$ o:
Como $or
consultas, IIUC, se procesan como consultas múltiples y, presumiblemente, pueden usar sus respectivos índices, ¿los resultados ordenados se fusionan de alguna manera o lo hacen $or
fuerzan un tipo de todos los resultados en la memoria? Si el primero, ¿cuál es la complejidad temporal de este proceso?
Nota: Esta respuesta se basa en MongoDB 3.2.4.
Vale la pena descubrir el uso de explain()
en MongoDB. La salida de explain()
de una consulta (por ejemplo, db.collection.explain().find(...)
) le permite verificar qué índice se usa en una consulta, y usar db.collection.explain(''executionStats'')
también le muestra si la consulta tiene éxito o falla debido a la limitación de SORT
en memoria.
$ en
Una consulta $in
se puede considerar como una serie de consultas de igualdad. Por ejemplo, {a: {$in: [1,3,5]}}
podría considerarse como {a:1}, {a:3}, {a:5}
. MongoDB ordenará el array $in
antes de continuar con la consulta, por lo que {$in: [3,5,1]}
no es diferente a {$in: [1,3,5]}
.
Supongamos que la colección tiene un índice de
{a:1, b:1}
Ordenando por
db.coll.find({a: {$in: [1,3,5]}}).sort({a:1})
MongoDB podrá usar el índice
{a:1,b:1}
, ya que esta consulta se puede considerar como una unión de consultas de{a:1}, {a:3}, {a:5}
. Ordenar por{a:1}
permite el uso del prefijo de índice , por lo que MongoDB no necesita realizar una ordenación en memoria.La misma situación también se aplica a la consulta:
db.coll.find({a: {$in: [1,3,5]} ,b:{$gte:1, $lt:2}}).sort({a:1})
dado que
sort({a:1})
también usa el prefijo del índice (a
en este caso), por lo tanto, no se requiere una etapaSORT
en la memoria.Ordenando por
b
Este es un caso más interesante en comparación con ordenar por
a
. Por ejemplo:db.coll.find({a: {$in: [1,3,5]}}).sort({b:1})
La salida de
explain()
de esta consulta tendrá una etapa llamadaSORT_MERGE
. Recuerde que la porciónfind()
de la consulta se puede considerar como{a:1}, {a:3}, {a:5}
.La consulta
db.coll.find({a:1}).sort({b:1})
no necesita tener una etapaSORT
en la memoria debido a la naturaleza del índice{a:1,b:1}
: es decir, MongoDB simplemente puede recorrer el índice (ordenado) y devolver documentos ordenados porb
después de satisfacer el parámetro de igualdad ena
. Por ejemplo, para cadaa
, hay muchasb
que ya están ordenadas porb
debido al índice.Usando
$in
, la consulta general se puede considerar como:-
db.coll.find({a:1}).sort({b:1})
-
db.coll.find({a:3}).sort({b:1})
-
db.coll.find({a:5}).sort({b:1})
- Tome los resultados de la consulta individual arriba, y realice una fusión usando el valor de
b
. La consulta no necesita una etapa de clasificación en memoria porque los resultados de consulta individuales ya están ordenados porb
. MongoDB solo necesita fusionar los resultados de la subconsulta (ya ordenados) en un solo resultado.
Del mismo modo, la consulta
db.coll.find({a: {$in: [1,3,5]} ,b:{$gte:1, $lt:2}}).sort({b:1})
también usa una etapa
SORT_MERGE
y es muy similar a la consulta anterior. La diferencia es que las consultas individuales generan documentos basados en un rango deb
(en lugar de cadab
) para cadaa
(que se ordenarán porb
debido al índice{a:1,b:1}
). Por lo tanto, la consulta no necesita una etapa de clasificación en memoria.-
$ o
Para que $or
consulta utilicen un índice, cada cláusula en la expresión $or
debe tener un índice asociado . Si se cumple este requisito, es posible que la consulta emplee una etapa SORT_MERGE
igual que una consulta $in
. Por ejemplo:
db.coll.explain().find({$or:[{a:1},{a:3},{a:5}]}).sort({b:1})
tendrá un plan de consulta, un uso del índice y SORT_MERGE
etapa SORT_MERGE
casi idénticos $in
ejemplo anterior $in
. Esencialmente, la consulta se puede pensar como:
-
db.coll.find({a:1}).sort({b:1})
-
db.coll.find({a:3}).sort({b:1})
-
db.coll.find({a:5}).sort({b:1})
- Tome los resultados de la consulta individual arriba, y realice una fusión usando el valor de
b
.
al igual que el $in
ejemplo anterior.
Sin embargo, esta consulta:
db.coll.explain().find({$or:[{a:1},{b:1}]}).sort({b:1})
no se puede usar ningún índice (ya que no tenemos el índice {b:1}
). Esta consulta dará como resultado un escaneo de colección, y en consecuencia tendrá una etapa de clasificación en memoria ya que no se usa ningún índice.
Sin embargo, si creamos el índice {b:1}
, la consulta procederá de la siguiente manera:
-
db.coll.find({a:1}).sort({b:1})
-
db.coll.find({b:1}).sort({b:1})
- Tome los resultados de la consulta individual arriba, y realice una fusión usando el valor de
b
(que ya está ordenado en ambas subconsultas, debido a los índices{a:1,b:1}
y{b:1}
).
y MongoDB combinará los resultados de las consultas {a:1}
y {b:1}
y realizará una combinación de los resultados. El proceso de fusión es el tiempo lineal, por ejemplo O(n)
.
En conclusión, en una consulta $or
, cada término debe tener un índice, incluida la etapa sort()
. De lo contrario, MongoDB tendrá que realizar una ordenación en memoria.