Condizione di query MongoDb sul confronto di 2 campi


108

Ho una collezione T, con 2 campi: Grade1e Grade2, e voglio selezionare quelli con condizione Grade1 > Grade2, come posso ottenere una query come in MySQL?

Select * from T Where Grade1 > Grade2

Risposte:


120

Puoi usare un $ dove. Tieni solo presente che sarà abbastanza lento (deve eseguire il codice Javascript su ogni record) quindi combinalo con le query indicizzate se puoi.

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

o più compatto:

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

UPD per mongodb v.3.6 +

puoi usare $exprcome descritto nella risposta recente


Oops, ho capito, solo javascript o altri script di shell congiunti, grazie a tutti e due!
Diego Cheng

16
Puoi farlo anche un po 'più compatto ...> db.T.find ({$ where: "this.Grade1> this.Grade2"});
Justin Jenkins

come potrei $where: function() { return this.Grade1 - this.Grade2 > variable }?
Luis González

quando ho provato db.T.find({$where: function() {return this.startDate == ISODate("2017-01-20T10:55:08.000Z");}});non restituisce nulla, anche uno dei documenti nella raccolta è ISODate("2017-01-20T10:55:08.000Z"). Ma <=e >=sembrano funzionare. qualche idea?
cateyes

@cateyes Un po 'in ritardo forse ... ma il puro javascript restituisce sempre false quando si esegue un == confronto tra 2 date. Mongo, tuttavia, ti consente di cercare corrispondenze esatte tra le date nelle query. Una soluzione alternativa è utilizzare .getTime () per convertire in millisecondi o qualsiasi altra cosa:this.startDate.getTime() == ISODate("2017-01-20T10:55:08.000Z").getTime()
leinaD_natipaC

53

È possibile utilizzare $ expr (operatore versione 3.6 mongo) per utilizzare le funzioni di aggregazione in query regolari.

Confronta query operatorsvs aggregation comparison operators.

Query regolare:

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

Query di aggregazione:

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

39

Se la tua query è composta solo $wheredall'operatore, puoi passare solo l'espressione JavaScript:

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

Per prestazioni migliori, eseguire un'operazione di aggregazione che dispone di una $redactpipeline per filtrare i documenti che soddisfano la condizione specificata.

La $redactpipeline incorpora la funzionalità $projecte $matchper implementare la redazione a livello di campo in cui restituirà tutti i documenti che corrispondono alla condizione utilizzando $$KEEPe rimuove dai risultati della pipeline quelli che non corrispondono utilizzando la $$PRUNEvariabile.


L'esecuzione della seguente operazione di aggregazione filtra i documenti in modo più efficiente rispetto all'utilizzo $whereper raccolte di grandi dimensioni poiché utilizza una singola pipeline e operatori MongoDB nativi, anziché valutazioni JavaScript con $where, che possono rallentare la query:

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

che è una versione più semplificata dell'incorporazione delle due pipeline $projecte $match:

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

Con MongoDB 3.4 e versioni successive:

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

l'ultimo non sembra funzionare con me. Il campo isGrade1Guesday viene aggiunto e valutato correttamente, ma per qualche motivo la query corrisponde a tutte le righe indipendentemente dal valore di isGrade1Guesday. Quale potrebbe essere la causa di questo comportamento? EDIT: Non importa, non stavo passando un array a aggregate () ma piuttosto ogni aggregazione come parametro stesso, l'ho perso.
ThatBrianDude

13

Nel caso in cui le prestazioni siano più importanti della leggibilità e fintanto che la tua condizione consiste in semplici operazioni aritmetiche, puoi utilizzare la pipeline di aggregazione. Per prima cosa, usa $ project per calcolare il lato sinistro della condizione (porta tutti i campi sul lato sinistro). Quindi usa $ match per confrontare con una costante e un filtro. In questo modo eviti l'esecuzione di javascript. Di seguito è riportato il mio test in 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)

Utilizzando aggregato:

%timeit -n1 -r1 list(coll.aggregate([
    {
        '$project': {
            'diff': {'$subtract': ['$Grade1', '$Grade2']},
            'Grade1': 1,
            'Grade2': 1
        }
    },
    {
        '$match': {'diff': {'$gt': 0}}
    }
]))

1 loop, meglio di 1: 192 ms per loop

Utilizzando find e $ where:

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

1 loop, meglio di 1: 4,54 s per loop

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.