Грешката е, защото вече не е масив, след като $unwind
и следователно вече не е валиден аргумент за $size
.
Изглежда се опитвате да „слеете“ няколко съществуващи отговора, без да разбирате какво правят. Това, което наистина искате тук, е $filter
и $size
db.collection.aggregate([
{ "$project": {
"total": {
"$size": {
"$filter": {
"input": "$Array",
"cond": { "$eq": [ "$$this.field1", "a" ] }
}
}
}
}}
])
Или „открийте отново колелото“, като използвате $reduce
:
db.collection.aggregate([
{ "$project": {
"total": {
"$reduce": {
"input": "$Array",
"initialValue": 0,
"in": {
"$sum": [
"$$value",
{ "$cond": [{ "$eq": [ "$$this.field1", "a" ] }, 1, 0] }
}
}
}
}}
])
Или за това, което се опитвахте да направите с $unwindкод>
, вие всъщност $group
отново, за да "преброите" колко съвпадения има:
db.collection.aggregate([
{ "$unwind": "$Array" },
{ "$match": { "Array.field1": "a" } },
{ "$group": {
"_id": "$_id",
"total": { "$sum": 1 }
}}
])
Първите две форми са "оптималните" за съвременните среди на MongoDB. Окончателният формуляр с $unwind
и $group
е „наследена“ конструкция, която наистина не е била необходима за този тип операция от MongoDB 2.6, макар и с някои малко по-различни оператори.
В първите две ние основно сравняваме field1
стойността на всеки елемент от масива, докато той все още е масив. И двете $filter
и $reduce
са модерни оператори, предназначени да работят със съществуващ масив на място. Същото сравнение се прави за всеки един, като се използва агрегацията $eq
оператор, който връща булева стойност въз основа на това дали дадените аргументи са "равни" или не. В този случай на всеки член на масива към очакваната стойност на "a"
.
В случай на $filter
, масивът всъщност остава непокътнат, с изключение на всички елементи, които не отговарят на предоставеното условие в "cond"
се премахват от масива. Тъй като все още имаме „масив“ като изход, можем да използваме $size
оператор за измерване на броя елементи на масива, останали след обработката на това филтърно условие.
$reduce
от друга страна работи чрез елементите на масива и доставя израз върху всеки елемент и съхранена стойност на „акумулатор“, която инициализирахме с "initialValue"
. В този случай същият $eq
тестът се прилага в рамките на $cond
оператор. Това е "троичен" или if/then/else
условен оператор, който позволява на тестван израз, който връща булева стойност, да върне then
стойност, когато е true
или друго
стойност при false
.
В този израз връщаме 1
или 0
съответно и предоставя общия резултат от добавянето на тази върната стойност и текущия "акумулатор" "$$value"
с $sum
оператор, за да ги добавите заедно.
Окончателният формуляр използва $unwind
върху масива. Това, което всъщност прави, е да деконструира членовете на масива, за да създаде "нов документ" за всеки член на масива и свързаните с него родителски полета в оригиналния документ. Това на практика „копира“ главния документ за всеки член на масива.
След като $unwind
структурата на документите се променя към "по-плоска" форма. Ето защо след това можете да направите последващото $matchкод>
етап на конвейер за премахване на несъвпадащите документи.
Това ни води до $group
който се прилага, за да „събере обратно“ цялата информация, свързана с общ ключ. В този случай това е _id
поле на оригиналния документ, което разбира се беше копирано във всеки документ, създаден от $unwind
. Докато се връщаме към този „общ ключ“ като единичен документ, можем да „преброим“ останалите „документи“, извлечени от масива, използвайки $sum
акумулатор.
Ако искаме да върнем оставащия „масив“, тогава можете да $push
и изградете отново масива само с останалите членове:
{ "$group": {
"_id": "$_id",
"Array": { "$push": "$Array" },
"total": { "$sum": 1 }
}}
Но, разбира се, вместо да използвате $size
в друг етап на конвейер можем просто все още да „броим“, както вече направихме с $sum