Non è ancora una bella query da eseguire, ma esiste un modo leggermente più moderno per farlo tramite $objectToArray
e $redact
db.collection.aggregate([
{ "$redact": {
"$cond": {
"if": {
"$eq": [
{ "$size": { "$objectToArray": "$value" } },
3
]
},
"then": "$$KEEP",
"else": "$$PRUNE"
}
}}
])
Dove $objectToArray
fondamentalmente costringe l'oggetto a formare un array, proprio come una combinazione di Object.keys()
e .map()
sarebbe in JavaScript.
Non è ancora un'idea fantastica poiché richiede la scansione dell'intera raccolta, ma almeno le operazioni del framework di aggregazione utilizzano il "codice nativo" anziché l'interpretazione JavaScript, come nel caso dell'utilizzo di $where
.
Quindi è generalmente consigliabile modificare la struttura dei dati e utilizzare un array naturale e le proprietà "size" memorizzate, ove possibile, al fine di rendere le operazioni di query più efficaci.
Sì, è possibile farlo ma non nel modo migliore. Il motivo è che stai essenzialmente utilizzando un $where
query dell'operatore che utilizza la valutazione JavaScript per abbinare i contenuti. Non è il modo più efficiente in quanto questo non può mai utilizzare un indice e deve testare tutti i documenti:
db.collection.find({ "$where": "return Object.keys(this.value).length == 3" })
Questo cerca la condizione corrispondente a "tre" elementi, quindi verranno restituiti solo due dei tuoi documenti elencati:
{ "_id" : "number1", "value" : { "a" : 1, "b" : 2, "f" : 5 } }
{ "_id" : "number2", "value" : { "e" : 2, "f" : 114, "h" : 12 } }
Oppure per "cinque" campi o più puoi fare più o meno lo stesso:
db.numbers.find({ "$where": "return Object.keys(this.value).length >= 5" })
Quindi gli argomenti di quell'operatore sono effettivamente istruzioni JavaScript che vengono valutate sul server per restituire dove true
.
Un modo più efficiente è memorizzare il "conteggio" degli elementi nel documento stesso. In questo modo puoi "indicizzare" questo campo e le query sono molto più efficienti in quanto ogni documento nella raccolta selezionata da altre condizioni non ha bisogno di essere scansionato per determinare la lunghezza:
{_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}
Quindi per ottenere i documenti con "cinque" elementi è sufficiente la semplice query:
db.collection.find({ "count": 5 })
Questa è generalmente la forma più ottimale. Ma un altro punto è che la struttura generale "Oggetto" di cui potresti essere soddisfatto dalla pratica generale non è qualcosa con cui MongoDB "gioca bene" in generale. Il problema è l'"attraversamento" degli elementi nell'oggetto, e in questo modo MongoDB è molto più felice quando si usa un "array". E anche in questa forma:
{
'_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 }
]
}
Quindi, se passi effettivamente a un formato "array" come quello, puoi fare un esatto lunghezza di un array con una versione di $size
operatore:
db.collection.find({ "values": { "$size": 5 } })
Quell'operatore può lavorare per un esatto valore per una lunghezza di matrice in quanto è una disposizione di base di ciò che può essere fatto con questo operatore. Ciò che non puoi fare è documentato in una corrispondenza "in-eguaglianza". Per questo è necessario il "framework di aggregazione" per MongoDB, che è un'alternativa migliore alle operazioni JavaScript e mapReduce:
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
}}
])
Quindi queste sono le alternative. È disponibile un metodo "nativo" per l'aggregazione e un tipo di matrice. Ma è abbastanza discutibile che la valutazione JavaScript sia anche "nativa" per MongoDB, ma non quindi implementata nel codice nativo.