Eu tenho uma coleção T
, com 2 campos: Grade1
e Grade2
, e quero selecionar aqueles com condição Grade1 > Grade2
, como posso obter uma consulta como no MySQL?
Select * from T Where Grade1 > Grade2
Eu tenho uma coleção T
, com 2 campos: Grade1
e Grade2
, e quero selecionar aqueles com condição Grade1 > Grade2
, como posso obter uma consulta como no MySQL?
Select * from T Where Grade1 > Grade2
Respostas:
Você pode usar um $ where. Esteja ciente de que será bastante lento (precisa executar o código Javascript em todos os registros), portanto, combine com as consultas indexadas, se puder.
db.T.find( { $where: function() { return this.Grade1 > this.Grade2 } } );
ou mais compacto:
db.T.find( { $where : "this.Grade1 > this.Grade2" } );
você pode usar $expr
conforme descrito na resposta recente
$where: function() { return this.Grade1 - this.Grade2 > variable }
?
db.T.find({$where: function() {return this.startDate == ISODate("2017-01-20T10:55:08.000Z");}});
não retornou nada, até mesmo um dos documentos da coleção ISODate("2017-01-20T10:55:08.000Z")
. Mas <=
e >=
parece funcionar. qualquer ideia?
this.startDate.getTime() == ISODate("2017-01-20T10:55:08.000Z").getTime()
Você pode usar $ expr (operador de versão 3.6 mongo) para usar funções de agregação em consultas regulares.
Compare query operators
vs aggregation comparison operators
.
Consulta regular:
db.T.find({$expr:{$gt:["$Grade1", "$Grade2"]}})
Consulta de agregação:
db.T.aggregate({$match:{$expr:{$gt:["$Grade1", "$Grade2"]}}})
Se sua consulta consistir apenas no $where
operador, você pode passar apenas a expressão JavaScript:
db.T.find("this.Grade1 > this.Grade2");
Para maior desempenho, execute uma operação agregada que tenha um $redact
pipeline para filtrar os documentos que satisfaçam a determinada condição.
O $redact
pipeline incorpora a funcionalidade de $project
e $match
para implementar a redação no nível do campo, onde retornará todos os documentos que correspondem à condição usando $$KEEP
e remove dos resultados do pipeline aqueles que não correspondem ao uso da $$PRUNE
variável.
Executar a operação de agregação a seguir filtra os documentos com mais eficiência do que usar $where
para grandes coleções, já que usa um único pipeline e operadores MongoDB nativos, em vez de avaliações de JavaScript com $where
, o que pode tornar a consulta mais lenta:
db.T.aggregate([
{
"$redact": {
"$cond": [
{ "$gt": [ "$Grade1", "$Grade2" ] },
"$$KEEP",
"$$PRUNE"
]
}
}
])
que é uma versão mais simplificada de incorporar os dois pipelines $project
e $match
:
db.T.aggregate([
{
"$project": {
"isGrade1Greater": { "$cmp": [ "$Grade1", "$Grade2" ] },
"Grade1": 1,
"Grade2": 1,
"OtherFields": 1,
...
}
},
{ "$match": { "isGrade1Greater": 1 } }
])
Com MongoDB 3.4 e mais recente:
db.T.aggregate([
{
"$addFields": {
"isGrade1Greater": { "$cmp": [ "$Grade1", "$Grade2" ] }
}
},
{ "$match": { "isGrade1Greater": 1 } }
])
Caso o desempenho seja mais importante do que a legibilidade e contanto que sua condição consista em operações aritméticas simples, você pode usar o pipeline de agregação. Primeiro, use $ project para calcular o lado esquerdo da condição (leve todos os campos para o lado esquerdo). Em seguida, use $ match para comparar com uma constante e um filtro. Desta forma, você evita a execução de javascript. Abaixo está meu teste em python:
import pymongo
from random import randrange
docs = [{'Grade1': randrange(10), 'Grade2': randrange(10)} for __ in range(100000)]
coll = pymongo.MongoClient().test_db.grades
coll.insert_many(docs)
Usando agregado:
%timeit -n1 -r1 list(coll.aggregate([
{
'$project': {
'diff': {'$subtract': ['$Grade1', '$Grade2']},
'Grade1': 1,
'Grade2': 1
}
},
{
'$match': {'diff': {'$gt': 0}}
}
]))
1 loop, melhor de 1: 192 ms por loop
Usando find e $ where:
%timeit -n1 -r1 list(coll.find({'$where': 'this.Grade1 > this.Grade2'}))
1 loop, melhor de 1: 4,54 s por loop