Het is nog steeds geen leuke query om uit te voeren, maar er is een iets modernere manier om het te doen via $objectToArray
en $redact
db.collection.aggregate([
{ "$redact": {
"$cond": {
"if": {
"$eq": [
{ "$size": { "$objectToArray": "$value" } },
3
]
},
"then": "$$KEEP",
"else": "$$PRUNE"
}
}}
])
Waar $objectToArray
dwingt het object in feite in een arrayvorm, net zoals een combinatie van Object.keys()
en .map()
zou in JavaScript.
Het is nog steeds geen fantastisch idee, omdat het wel de hele collectie moet scannen, maar de bewerkingen van het aggregatieraamwerk gebruiken in ieder geval "native code" in tegenstelling tot JavaScript-interpretatie zoals het geval is bij het gebruik van $where
.
Het is dus over het algemeen nog steeds aan te raden om de gegevensstructuur te wijzigen en waar mogelijk een natuurlijke array te gebruiken, evenals opgeslagen "grootte" -eigenschappen om de meest effectieve querybewerkingen uit te voeren.
Ja, het is mogelijk om te doen, maar niet op de mooiste manier. De reden hiervoor is dat je in wezen een $waar
operatorquery die JavaScript-evaluatie gebruikt om de inhoud te matchen. Niet de meest efficiënte manier omdat dit nooit een index kan gebruiken en alle documenten moeten testen:
db.collection.find({ "$where": "return Object.keys(this.value).length == 3" })
Dit zoekt naar de voorwaarde die overeenkomt met "drie" elementen, dan zouden slechts twee van uw vermelde documenten worden geretourneerd:
{ "_id" : "number1", "value" : { "a" : 1, "b" : 2, "f" : 5 } }
{ "_id" : "number2", "value" : { "e" : 2, "f" : 114, "h" : 12 } }
Of voor "vijf" velden of meer kunt u ongeveer hetzelfde doen:
db.numbers.find({ "$where": "return Object.keys(this.value).length >= 5" })
Dus de argumenten voor die operator zijn in feite JavaScript-instructies die op de server worden geëvalueerd om terug te keren waar true
.
Een efficiëntere manier is om de "telling" van de elementen in het document zelf op te slaan. Op deze manier kunt u dit veld "indexeren" en de zoekopdrachten zijn veel efficiënter omdat elk document in de collectie geselecteerd door andere voorwaarden niet hoeft te worden gescand om de lengte te bepalen:
{_id:'number1', value:{'a':1, 'b':2, 'f':5} count: 3},
{_id:'number2', value:{'e':2, 'f':114, 'h':12}, count: 3},
{_id:'number3', value:{'i':2, 'j':22, 'z':12, 'za':111, 'zb':114}, count: 5}
Om vervolgens de documenten met "vijf" elementen te krijgen, hebt u alleen de eenvoudige vraag nodig:
db.collection.find({ "count": 5 })
Dat is over het algemeen de meest optimale vorm. Maar een ander punt is dat de algemene "Object"-structuur waar u misschien blij mee bent uit de algemene praktijk, niet iets is waar MongoDB in het algemeen "goed mee speelt". Het probleem is het "traversal" van elementen in het object, en op deze manier is MongoDB veel gelukkiger als je een "array" gebruikt. En zelfs in deze vorm:
{
'_id': 'number1',
'values':[
{ 'key': 'a', 'value': 1 },
{ 'key': 'b', 'value': 2 },
{ 'key': 'f', 'value': 5 }
],
},
{
'_id': 'number2',
'values':[
{ 'key': 'e', 'value': 2 },
{ 'key': 'f', 'value': 114 },
{ 'key': 'h', 'value': 12 }
],
},
{
'_id':'number3',
'values': [
{ 'key': 'i', 'values': 2 },
{ 'key': 'j', 'values': 22 },
{ 'key': 'z'' 'values': :12 },
{ 'key': 'za', 'values': 111 },
{ 'key': 'zb', 'values': 114 }
]
}
Dus als u daadwerkelijk overschakelt naar een dergelijke "array"-indeling, kunt u een exacte . doen lengte van een array met één versie van de $ maat
operator:
db.collection.find({ "values": { "$size": 5 } })
Die operator kan werken voor een exacte waarde voor een arraylengte, want dat is een basisvoorziening van wat er met deze operator kan worden gedaan. Wat u niet kunt doen, wordt gedocumenteerd in een "ongelijkheid"-wedstrijd. Daarvoor heb je het "aggregatieframework" voor MongoDB nodig, dat een beter alternatief is voor JavaScript en mapReduce-bewerkingen:
db.collection.aggregate([
// Project a size of the array
{ "$project": {
"values": 1,
"size": { "$size": "$values" }
}},
// Match on that size
{ "$match": { "size": { "$gte": 5 } } },
// Project just the same fields
{{ "$project": {
"values": 1
}}
])
Dat zijn dus de plaatsvervangers. Er is een "native" methode beschikbaar voor aggregatie en een arraytype. Maar het is redelijk betwistbaar dat de JavaScript-evaluatie ook "native" is voor MongoDB, maar daarom niet geïmplementeerd in native code.