Все още не е приятна заявка за изпълнение, но има малко по-модерен начин да го направите чрез $objectToArray
и $redact
db.collection.aggregate([
{ "$redact": {
"$cond": {
"if": {
"$eq": [
{ "$size": { "$objectToArray": "$value" } },
3
]
},
"then": "$$KEEP",
"else": "$$PRUNE"
}
}}
])
Където $objectToArray
основно принуждава обекта да се превърне във форма на масив, подобно на комбинация от Object.keys()
и .map()
би в JavaScript.
Това все още не е фантастична идея, тъй като изисква сканиране на цялата колекция, но поне операциите на рамката за агрегиране използват „местен код“ за разлика от интерпретацията на JavaScript, какъвто е случаят с $where
.
Така че все още е препоръчително да промените структурата на данните и да използвате естествен масив, както и съхранени свойства на „размера“, където е възможно, за да направите най-ефективните операции за заявки.
Да, възможно е да се направи, но не по най-добрия начин. Причината за това е, че по същество използвате $where
операторска заявка, която използва оценка на JavaScript, за да съответства на съдържанието. Не е най-ефективният начин, тъй като това никога не може да използва индекс и трябва да тества всички документи:
db.collection.find({ "$where": "return Object.keys(this.value).length == 3" })
Това търси условието, съответстващо на "три" елемента, след което ще бъдат върнати само два от вашите изброени документи:
{ "_id" : "number1", "value" : { "a" : 1, "b" : 2, "f" : 5 } }
{ "_id" : "number2", "value" : { "e" : 2, "f" : 114, "h" : 12 } }
Или за "пет" полета или повече можете да направите почти същото:
db.numbers.find({ "$where": "return Object.keys(this.value).length >= 5" })
Така че аргументите към този оператор са ефективно изрази на JavaScript, които се оценяват на сървъра, за да върнат къде true
.
По-ефективен начин е да съхранявате "броя" на елементите в самия документ. По този начин можете да "индексирате" това поле и заявките са много по-ефективни, тъй като всеки документ в колекцията, избран от други условия, не е необходимо да се сканира, за да се определи дължината:
{_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}
След това, за да получите документите с "пет" елемента, ви е необходима само простата заявка:
db.collection.find({ "count": 5 })
По принцип това е най-оптималната форма. Но друг момент е, че общата структура „Обект“, от която може да сте доволни от общата практика, не е нещо, с което MongoDB „играе добре“ като цяло. Проблемът е "преминаване" на елементи в обекта и по този начин MongoDB е много по-щастлив, когато използвате "масив". И дори в тази форма:
{
'_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 }
]
}
Така че, ако действително превключите към формат "масив" като този, тогава можете да направите точен дължина на масив с една версия на $size
оператор:
db.collection.find({ "values": { "$size": 5 } })
Този оператор може да работи за точна стойност за дължина на масив, тъй като това е основно условие за това, което може да се направи с този оператор. Това, което не можете да направите, е документирано в съвпадение на „неравенство“. За целта се нуждаете от „рамката за агрегиране“ за MongoDB, която е по-добра алтернатива на JavaScript и операциите 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
}}
])
Така че това са алтернативите. Има "роден" метод, наличен за агрегиране и тип масив. Но е доста спорно, че оценката на JavaScript също е „местна“ за MongoDB, просто не е внедрена в собствен код.