Nadal nie jest to przyjemne zapytanie do uruchomienia, ale istnieje nieco bardziej nowoczesny sposób na wykonanie tego za pomocą $objectToArray
i $redact
db.collection.aggregate([
{ "$redact": {
"$cond": {
"if": {
"$eq": [
{ "$size": { "$objectToArray": "$value" } },
3
]
},
"then": "$$KEEP",
"else": "$$PRUNE"
}
}}
])
Gdzie $objectToArray
zasadniczo przekształca obiekt w formę tablicy, podobnie jak kombinacja Object.keys()
i .map()
byłby w JavaScript.
To wciąż nie jest fantastyczny pomysł, ponieważ wymaga przeskanowania całej kolekcji, ale przynajmniej operacje na frameworku agregacji używają "kodu natywnego" w przeciwieństwie do interpretacji JavaScript, jak ma to miejsce przy użyciu $where
.
Dlatego nadal zaleca się zmianę struktury danych i użycie naturalnej tablicy, a także przechowywanych właściwości „rozmiaru”, tam gdzie to możliwe, aby wykonać najbardziej efektywne operacje zapytań.
Tak, jest to możliwe, ale nie w najmilszy sposób. Powodem tego jest to, że zasadniczo używasz $where
zapytanie operatora, które używa oceny JavaScript do dopasowania zawartości. Nie jest to najskuteczniejszy sposób, ponieważ nigdy nie można użyć indeksu i trzeba przetestować wszystkie dokumenty:
db.collection.find({ "$where": "return Object.keys(this.value).length == 3" })
Spowoduje to wyszukanie warunku pasującego do „trzech” elementów, a następnie zwrócone zostaną tylko dwa z wymienionych dokumentów:
{ "_id" : "number1", "value" : { "a" : 1, "b" : 2, "f" : 5 } }
{ "_id" : "number2", "value" : { "e" : 2, "f" : 114, "h" : 12 } }
Lub dla "pięciu" pól lub więcej możesz zrobić to samo:
db.numbers.find({ "$where": "return Object.keys(this.value).length >= 5" })
Tak więc argumenty tego operatora są de facto wyrażeniami JavaScript, które są oceniane na serwerze i zwracają gdzie true
.
Bardziej wydajnym sposobem jest przechowywanie „liczby” elementów w samym dokumencie. W ten sposób możesz "indeksować" to pole, a zapytania są znacznie bardziej wydajne, ponieważ każdy dokument w zbiorze wybranym przez inne warunki nie musi być skanowany w celu określenia długości:
{_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}
Następnie, aby uzyskać dokumenty z "pięcioma" elementami, potrzebujesz tylko prostego zapytania:
db.collection.find({ "count": 5 })
To jest ogólnie najbardziej optymalna forma. Ale inną kwestią jest to, że ogólna struktura „obiektu”, z której możesz być zadowolony z ogólnej praktyki, nie jest czymś, z czym MongoDB „ogólnie dobrze się bawi”. Problemem jest „przemierzanie” elementów w obiekcie iw ten sposób MongoDB jest znacznie szczęśliwsze, gdy używasz „tablicy”. A nawet w tej formie:
{
'_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 }
]
}
Więc jeśli faktycznie przełączysz się na taki format „tablicy”, możesz zrobić dokładnie długość tablicy z jedną wersją $size
operator:
db.collection.find({ "values": { "$size": 5 } })
Ten operator może pracować dla dokładnie wartość dla długości tablicy, ponieważ jest to podstawowe określenie tego, co można zrobić za pomocą tego operatora. To, czego nie możesz zrobić, jest udokumentowane w meczu „nierówności”. W tym celu potrzebujesz „struktury agregacji” dla MongoDB, która jest lepszą alternatywą dla operacji JavaScript i 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
}}
])
Więc to są zastępcy. Dostępna jest „natywna” metoda agregacji i typ tablicy. Ale jest dość dyskusyjne, że ocena JavaScript jest również "natywna" dla MongoDB, po prostu nie została zaimplementowana w kodzie natywnym.