MongoDB
 sql >> Base de Dados >  >> NoSQL >> MongoDB

Como a classificação funciona com consultas `$or` e `$in` no MongoDB?


Observação: Esta resposta é baseada no MongoDB 3.2.4.

Vale a pena descobrir o uso de explain() no MongoDB. O explain() saída de uma consulta (por exemplo, db.collection.explain().find(...) ) permite verificar qual índice é usado em uma consulta e usar db.collection.explain('executionStats') também mostrará se a consulta foi bem-sucedida ou falhou devido a SORT na memória limitação.

$in

Um $in pode ser considerada como uma série de consultas de igualdade. Por exemplo, {a: {$in: [1,3,5]}} poderia ser considerado como {a:1}, {a:3}, {a:5} . O MongoDB classificará o $in array antes de prosseguir com a consulta, para que {$in: [3,5,1]} não é diferente de {$in: [1,3,5]} .

Vamos supor que a coleção tenha um índice de
{a:1, b:1}

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

    O MongoDB poderá usar o {a:1,b:1} index, pois essa consulta pode ser considerada uma união de {a:1}, {a:3}, {a:5} consultas. Classificando por {a:1} permite o uso de prefixo de índice , portanto, o MongoDB não precisa executar uma classificação na memória.

    A mesma situação também se aplica à consulta:
      db.coll.find({a: {$in: [1,3,5]} ,b:{$gte:1, $lt:2}}).sort({a:1})
    

    desde sort({a:1}) também usa o prefixo de índice (a neste caso), um SORT na memória estágio, portanto, não é necessário.

  • Classificando por b

    Este é um caso mais interessante comparado à classificação por a . Por exemplo:
      db.coll.find({a: {$in: [1,3,5]}}).sort({b:1})
    

    A explain() a saída desta consulta terá um estágio chamado SORT_MERGE . Lembre-se que o find() parte da consulta pode ser considerada como {a:1}, {a:3}, {a:5} .

    A consulta db.coll.find({a:1}).sort({b:1}) não precisa ter um SORT na memória estágio devido à natureza do {a:1,b:1} index:ou seja, o MongoDB pode simplesmente percorrer o índice (classificado) e retornar documentos classificados por b depois de satisfazer o parâmetro de igualdade em a . Por exemplo, para cada a , existem muitos b que já estão ordenados por b devido ao índice.

    Usando $in , a consulta geral pode ser considerada 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})
    • Pegue os resultados individuais da consulta acima e faça uma mesclagem usando o valor de b . A consulta não precisa de um estágio de classificação na memória porque os resultados da consulta individual já estão classificados por b . O MongoDB só precisa mesclar os resultados da subconsulta (já classificados) em um único resultado.

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

    também usa um SORT_MERGE stage e é muito semelhante à consulta acima. A diferença é que as consultas individuais produzem documentos com base em um intervalo de b (em vez de todos b ) para cada a (que será ordenado por b devido ao índice {a:1,b:1} ). Portanto, a consulta não precisa de um estágio de classificação na memória.

$ou

Para um $or query para usar um índice, todas as cláusulas no $or expressão deve ter um índice associado a ela . Se este requisito for atendido, é possível que a consulta empregue um SORT_MERGE stage como um $in consulta. Por exemplo:
db.coll.explain().find({$or:[{a:1},{a:3},{a:5}]}).sort({b:1})

terá um plano de consulta quase idêntico, uso de índice e SORT_MERGE estágio como no $in exemplo acima. Essencialmente, a consulta pode ser pensada 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})
  • Pegue os resultados individuais da consulta acima e faça uma mesclagem usando o valor de b .

assim como o $in exemplo antes.

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

não pode usar nenhum índice (já que não temos o {b:1} índice). Essa consulta resultará em uma verificação de coleção e, consequentemente, terá um estágio de classificação na memória uma vez que nenhum índice é usado.

Se, no entanto, criarmos o índice {b:1} , a consulta prosseguirá assim:
  • db.coll.find({a:1}).sort({b:1})
  • db.coll.find({b:1}).sort({b:1})
  • Pegue os resultados individuais da consulta acima e faça uma mesclagem usando o valor de b (que já está ordenado em ambas as subconsultas, devido aos índices {a:1,b:1} e {b:1} ).

e o MongoDB combinará os resultados de {a:1} e {b:1} consultas e realizar uma mesclagem nos resultados. O processo de fusão é em tempo linear, e. O(n) .

Em conclusão, em um $or consulta, cada termo deve ter um índice, incluindo o sort() palco. Caso contrário, o MongoDB terá que realizar uma classificação na memória.