Felet beror på att det inte längre är en array efter att du $unwind
och därför inte längre ett giltigt argument för $size
.
Du verkar försöka "sammanfoga" ett par befintliga svar utan att förstå vad de gör. Det du verkligen vill ha här är $filter
och $size
db.collection.aggregate([
{ "$project": {
"total": {
"$size": {
"$filter": {
"input": "$Array",
"cond": { "$eq": [ "$$this.field1", "a" ] }
}
}
}
}}
])
Eller "uppfinna hjulet på nytt" med $reduce
:
db.collection.aggregate([
{ "$project": {
"total": {
"$reduce": {
"input": "$Array",
"initialValue": 0,
"in": {
"$sum": [
"$$value",
{ "$cond": [{ "$eq": [ "$$this.field1", "a" ] }, 1, 0] }
}
}
}
}}
])
Eller för vad du försökte göra med $unwind
, du faktiskt $group
igen för att "räkna" hur många matcher det var:
db.collection.aggregate([
{ "$unwind": "$Array" },
{ "$match": { "Array.field1": "a" } },
{ "$group": {
"_id": "$_id",
"total": { "$sum": 1 }
}}
])
De två första formerna är de "optimala" för moderna MongoDB-miljöer. Det slutliga formuläret med $unwind
och $group
är en "legacy" konstruktion som verkligen inte har varit nödvändig för den här typen av operation sedan MongoDB 2.6, dock med några lite annorlunda operatörer.
I de två första jämför vi i princip field1
värdet för varje arrayelement medan det fortfarande är en array. Båda $filter
och $reduce
är moderna operatörer designade för att arbeta med en befintlig array på plats. Samma jämförelse görs på var och en med aggregeringen $eq
operator som returnerar ett booleskt värde baserat på om de angivna argumenten är "lika" eller inte. I detta fall på varje arraymedlem till det förväntade värdet "a"
.
I fallet med $filter
, förblir matrisen faktiskt intakt förutom alla element som inte uppfyllde det angivna villkoret i "cond"
tas bort från arrayen. Eftersom vi fortfarande har en "array" som utdata kan vi sedan använda $storlek
operatorn för att mäta antalet arrayelement som finns kvar efter att filtervillkoret bearbetades.
$reduce
å andra sidan arbetar genom arrayelementen och tillhandahåller ett uttryck över varje element och ett lagrat "ackumulator"-värde, som vi initierade med "initialValue"
. I det här fallet samma $eq
testet tillämpas inom $cond
operatör. Detta är en "ternär" eller if/then/else
villkorlig operator som tillåter att ett testat uttryck som returnerar ett booleskt värde returnerar then
värde när true
eller annat
värde när false
.
I det uttrycket returnerar vi 1
eller 0
respektive och ange det totala resultatet av att lägga till det returnerade värdet och den aktuella "ackumulatorn" "$$värde"
med $sum
operatör för att lägga ihop dessa.
Det slutliga formuläret används $unwind
på arrayen. Vad detta faktiskt gör är att dekonstruera arraymedlemmarna för att skapa ett "nytt dokument" för varje arraymedlem och dess relaterade överordnade fält i originaldokumentet. Detta "kopierar" effektivt huvuddokumentet för varje arraymedlem.
När du $unwind
dokumentens struktur ändras till en "plattare" form. Det är därför du sedan kan göra den efterföljande $match
pipeline för att ta bort de omatchade dokumenten.
Detta för oss till $group
som används för att "föra samman" all information som är relaterad till en gemensam nyckel. I det här fallet är det _id
fältet i originaldokumentet, som naturligtvis kopierades till varje dokument som producerats av $unwind
. När vi går tillbaka till denna "gemensamma nyckel" som ett enda dokument, kan vi "räkna" de återstående "dokumenten" som extraherats från arrayen med hjälp av $sum
ackumulator.
Om vi ville ha tillbaka den återstående "arrayen" kan du $push
och bygg om arrayen med bara de återstående medlemmarna:
{ "$group": {
"_id": "$_id",
"Array": { "$push": "$Array" },
"total": { "$sum": 1 }
}}
Men naturligtvis istället för att använda $size
i ett annat steg i pipeline kan vi helt enkelt fortfarande "räkna" som vi redan gjorde med $summa