Condición de consulta de MongoDb al comparar 2 campos

108

Tengo una colección T, con 2 campos: Grade1y Grade2, y quiero seleccionar aquellos con condición Grade1 > Grade2, ¿cómo puedo obtener una consulta como en MySQL?

Select * from T Where Grade1 > Grade2
Diego Cheng
fuente

Respuestas:

120

Puede usar $ where. Solo tenga en cuenta que será bastante lento (tiene que ejecutar código Javascript en cada registro), así que combine con consultas indexadas si puede.

db.T.find( { $where: function() { return this.Grade1 > this.Grade2 } } );

o más compacto:

db.T.find( { $where : "this.Grade1 > this.Grade2" } );

UPD para mongodb v.3.6 +

puede usar $exprcomo se describe en la respuesta reciente

Ian
fuente
Vaya, lo entiendo, solo javascript de uso conjunto u otros scripts de shell, ¡gracias a los dos!
Diego Cheng
16
También puede hacer esto un poco más compacto ...> db.T.find ({$ where: "this.Grade1> this.Grade2"});
Justin Jenkins
¿cómo podría hacerlo $where: function() { return this.Grade1 - this.Grade2 > variable }?
Luis González
cuando lo intenté, db.T.find({$where: function() {return this.startDate == ISODate("2017-01-20T10:55:08.000Z");}});no devuelve nada, incluso uno de los documentos de la colección es ISODate("2017-01-20T10:55:08.000Z"). Pero <=y >=parece trabajo. ¿alguna idea?
cateyes
@cateyes Un poco tarde quizás ... pero javascript puro siempre devuelve falso cuando se hace una comparación == entre 2 fechas. Mongo, sin embargo, le permite buscar coincidencias exactas entre fechas en las consultas. Una solución alternativa es usar .getTime () para convertir a milisegundos o lo que sea:this.startDate.getTime() == ISODate("2017-01-20T10:55:08.000Z").getTime()
leinaD_natipaC
53

Puede usar $ expr (operador de versión 3.6 de mongo) para usar funciones de agregación en una consulta normal.

Comparar query operatorsvs aggregation comparison operators.

Consulta regular:

db.T.find({$expr:{$gt:["$Grade1", "$Grade2"]}})

Consulta de agregación:

db.T.aggregate({$match:{$expr:{$gt:["$Grade1", "$Grade2"]}}})
svr
fuente
39

Si su consulta consta solo del $whereoperador, puede pasar solo la expresión JavaScript:

db.T.find("this.Grade1 > this.Grade2");

Para un mayor rendimiento, ejecute una operación agregada que tenga un $redact canalización para filtrar los documentos que satisfacen la condición dada.

La $redactcanalización incorpora la funcionalidad de $projecte $matchimplementar la redacción a nivel de campo, donde devolverá todos los documentos que coincidan con la condición que usa $$KEEPy elimina de los resultados de la canalización aquellos que no coincidan con la $$PRUNEvariable.


Al ejecutar la siguiente operación agregada, se filtran los documentos de manera más eficiente que con $wherecolecciones grandes, ya que se usa una única canalización y operadores nativos de MongoDB, en lugar de evaluaciones de JavaScript con $where, lo que puede ralentizar la consulta:

db.T.aggregate([
    {
        "$redact": {
            "$cond": [
                { "$gt": [ "$Grade1", "$Grade2" ] },
                "$$KEEP",
                "$$PRUNE"
            ]
        }
    }
])

que es una versión más simplificada de incorporar las dos canalizaciones $projecty $match:

db.T.aggregate([
    {
        "$project": {
            "isGrade1Greater": { "$cmp": [ "$Grade1", "$Grade2" ] },
            "Grade1": 1,
            "Grade2": 1,
            "OtherFields": 1,
            ...
        }
    },
    { "$match": { "isGrade1Greater": 1 } }
])

Con MongoDB 3.4 y versiones posteriores:

db.T.aggregate([
    {
        "$addFields": {
            "isGrade1Greater": { "$cmp": [ "$Grade1", "$Grade2" ] }
        }
    },
    { "$match": { "isGrade1Greater": 1 } }
])
chridam
fuente
el último no parece funcionar conmigo. El campo isGrade1Greater se agrega y evalúa correctamente pero, por alguna razón, la consulta coincide con todas las filas sin importar el valor de isGrade1Greater. ¿Cuál podría ser la causa de este comportamiento? EDITAR: No importa, no estaba pasando una matriz a aggregate (), sino que cada agregación como un parámetro en sí misma, me lo perdí.
ThatBrianDude
13

En caso de que el rendimiento sea más importante que la legibilidad y siempre que su condición consista en operaciones aritméticas simples, puede usar la canalización de agregación. Primero, use $ project para calcular el lado izquierdo de la condición (lleve todos los campos al lado izquierdo). Luego use $ match para comparar con una constante y un filtro. De esta forma evita la ejecución de JavaScript. A continuación se muestra mi prueba en 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 bucle, lo mejor de 1: 192 ms por bucle

Usando buscar y $ donde:

%timeit -n1 -r1 list(coll.find({'$where': 'this.Grade1 > this.Grade2'}))

1 bucle, lo mejor de 1: 4,54 s por bucle

Sina
fuente