Ho una collezione T
, con 2 campi: Grade1
e Grade2
, e voglio selezionare quelli con condizione Grade1 > Grade2
, come posso ottenere una query come in MySQL?
Select * from T Where Grade1 > Grade2
Ho una collezione T
, con 2 campi: Grade1
e Grade2
, e voglio selezionare quelli con condizione Grade1 > Grade2
, come posso ottenere una query come in MySQL?
Select * from T Where Grade1 > Grade2
Risposte:
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" } );
puoi usare $expr
come descritto nella risposta recente
$where: function() { return this.Grade1 - this.Grade2 > variable }
?
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?
this.startDate.getTime() == ISODate("2017-01-20T10:55:08.000Z").getTime()
È possibile utilizzare $ expr (operatore versione 3.6 mongo) per utilizzare le funzioni di aggregazione in query regolari.
Confronta query operators
vs aggregation comparison operators
.
Query regolare:
db.T.find({$expr:{$gt:["$Grade1", "$Grade2"]}})
Query di aggregazione:
db.T.aggregate({$match:{$expr:{$gt:["$Grade1", "$Grade2"]}}})
Se la tua query è composta solo $where
dall'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 $redact
pipeline per filtrare i documenti che soddisfano la condizione specificata.
La $redact
pipeline incorpora la funzionalità $project
e $match
per implementare la redazione a livello di campo in cui restituirà tutti i documenti che corrispondono alla condizione utilizzando $$KEEP
e rimuove dai risultati della pipeline quelli che non corrispondono utilizzando la $$PRUNE
variabile.
L'esecuzione della seguente operazione di aggregazione filtra i documenti in modo più efficiente rispetto all'utilizzo $where
per 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 $project
e $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 } }
])
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