Come creare una query mongodb che trova se un numero è tra due campi?
-
21-12-2019 - |
Domanda
Gli oggetti nel database hanno un'array 'Properties', che può contenere oggetti diversi.Alcuni di loro presentano numeri o gamme e sembrano:
{'value': 10}
.
o
{'minValue': 4, 'maxValue': 8}
.
Quando interroga la collezione per un numero specifico, come X, voglio trovare tutti i documenti in cui entrambi i dati equivalgono x o minvalue <= x <= maxvalue.Il mio primo tentativo di una query sembra
db.Pool0.find({$or: [{'properties.value': X}, {'properties.minValue': {$lte: X}, 'properties.maxValue': {$gte: X}}]}, {'_id': 1}).pretty()
.
L'inconveniente è che se la matrice delle proprietà tiene più oggetti che specificano minvalue e maxvalue, x può essere tra nessuno di essi.Ad esempio
"properties" : [
{
"minValue" : 4,
"maxValue" : 6
},
{
"minValue" : 10,
"maxValue" : 20
}
]
.
si abbinano per X= 8. La query può essere migliorata in modo che la struttura dell'oggetto all'interno delle proprietà sia rispettata?
Soluzione
Si vuole praticamente usare $elemMatch
combinato con un $or
CondizioneIn questa forma:
db.collection.find({
"$or": [
{
"properties": { "$elemMatch": {
"minValue": { "$lte": 8 },
"maxValue": { "$gte": 8 }
}}
},
{
"properties.value": 8
}
]
})
.
che copre riunisce i documenti che contengono l'intervallo e il possibile altro nome chiave per il campo nelle proprietà.
Ma tieni presente che i documenti corrispondenti sono diversi per abbinare gli elementi in un array.Quindi se ti aspetti solo gli elementi dell'array che corrispondono ad essere restituiti e ne hai più di uno, quindi usi il modulo aggregato:
db.collection.aggregate([
// Matching documents is still good practice
{ "$match": {
"$or": [
{
"properties": { "$elemMatch": {
"minValue": { "$lte": 8 },
"maxValue": { "$gte": 8 }
}}
},
{
"properties.value": 8
}
]
}},
// Unwind to de-normalize
{ "$unwind": "$properties" },
// Then match to filter the elements
{ "$match": {
"$or": [
{
"properties.minValue": { "$lte": 8 },
"properties.maxValue": { "$gte": 8 }
},
{ "properties.value": 8 }
]
}},
// Reconstruct the filtered array
{ "$group": {
"_id": "$_id",
"properties": { "$push": "$properties" }
}}
])
.
Ma se ci sarà solo una corrispondenza di cui sei sicuro di usare semplicemente la proiezione con la ricerca:
db.collection.find(
{
"$or": [
{
"properties": { "$elemMatch": {
"minValue": { "$lte": 8 },
"maxValue": { "$gte": 8 }
}}
},
{
"properties.value": 8
}
]
},
{ "properties.$": 1 }
)
.