Stále to není pěkný dotaz na spuštění, ale existuje trochu modernější způsob, jak to udělat pomocí $objectToArray
a $redact
db.collection.aggregate([
{ "$redact": {
"$cond": {
"if": {
"$eq": [
{ "$size": { "$objectToArray": "$value" } },
3
]
},
"then": "$$KEEP",
"else": "$$PRUNE"
}
}}
])
Kde $objectToArray
v podstatě vnucuje objekt do tvaru pole, podobně jako kombinace Object.keys()
a .map()
by v JavaScriptu.
Stále to není fantastický nápad, protože to vyžaduje skenování celé kolekce, ale alespoň operace agregačního rámce používají "nativní kód" na rozdíl od interpretace JavaScriptu, jako je tomu v případě $where
.
Stále je tedy obecně vhodné změnit datovou strukturu a tam, kde je to možné, používat přirozené pole a také uložené vlastnosti „velikost“, aby byly operace dotazů co nejúčinnější.
Ano, je to možné, ale ne tím nejlepším způsobem. Důvodem je, že v podstatě používáte $where
dotaz operátora, který používá vyhodnocení JavaScriptu, aby odpovídal obsahu. Není to nejúčinnější způsob, protože to nikdy nemůže použít index a potřebuje otestovat všechny dokumenty:
db.collection.find({ "$where": "return Object.keys(this.value).length == 3" })
Toto hledá podmínku odpovídající "třem" prvkům, pak by byly vráceny pouze dva z vašich uvedených dokumentů:
{ "_id" : "number1", "value" : { "a" : 1, "b" : 2, "f" : 5 } }
{ "_id" : "number2", "value" : { "e" : 2, "f" : 114, "h" : 12 } }
Nebo pro "pět" polí nebo více můžete udělat totéž:
db.numbers.find({ "$where": "return Object.keys(this.value).length >= 5" })
Takže argumenty pro tento operátor jsou v podstatě příkazy JavaScriptu, které jsou na serveru vyhodnoceny tak, aby se vrátily tam, kde je true
.
Efektivnějším způsobem je uložit „počet“ prvků do samotného dokumentu. Tímto způsobem můžete toto pole „indexovat“ a dotazy jsou mnohem efektivnější, protože každý dokument ve sbírce vybrané jinými podmínkami nemusí být pro určení délky skenován:
{_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}
K získání dokumentů s „pěti“ prvky pak potřebujete pouze jednoduchý dotaz:
db.collection.find({ "count": 5 })
To je obecně nejoptimálnější forma. Dalším bodem však je, že obecná struktura „Object“, se kterou byste mohli být spokojeni z obecné praxe, není něco, s čím si MongoDB obecně „dobře hraje“. Problémem je „procházení“ prvků v objektu a tímto způsobem je MongoDB mnohem šťastnější, když používáte „pole“. A to dokonce v této podobě:
{
'_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 }
]
}
Pokud tedy skutečně přepnete na formát „pole“, jako je tento, můžete provést přesné délka pole s jednou verzí $size
operátor:
db.collection.find({ "values": { "$size": 5 } })
Tento operátor může pracovat pro přesné hodnotu pro délku pole, protože to je základní ustanovení toho, co lze s tímto operátorem dělat. To, co nemůžete udělat, je zdokumentováno v zápase „nerovnosti“. K tomu potřebujete „agregační rámec“ pro MongoDB, což je lepší alternativa k operacím JavaScript a 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
}}
])
To jsou tedy náhradníci. Pro agregaci a typ pole je k dispozici "nativní" metoda. Je však docela sporné, že hodnocení JavaScriptu je také „nativní“ pro MongoDB, jen proto není implementováno v nativním kódu.