Fejlen skyldes, at det ikke længere er et array efter du $unwind
og derfor ikke længere et gyldigt argument til $size
.
Du ser ud til at forsøge at "sammenflette" et par eksisterende svar uden at forstå, hvad de gør. Det, du virkelig ønsker her, er $filter
og $size
db.collection.aggregate([
{ "$project": {
"total": {
"$size": {
"$filter": {
"input": "$Array",
"cond": { "$eq": [ "$$this.field1", "a" ] }
}
}
}
}}
])
Eller "genopfind hjulet" ved hjælp af $reduce
:
db.collection.aggregate([
{ "$project": {
"total": {
"$reduce": {
"input": "$Array",
"initialValue": 0,
"in": {
"$sum": [
"$$value",
{ "$cond": [{ "$eq": [ "$$this.field1", "a" ] }, 1, 0] }
}
}
}
}}
])
Eller for det, du prøvede at gøre med $unwind
, du faktisk $group
igen for at "tælle" hvor mange kampe der var:
db.collection.aggregate([
{ "$unwind": "$Array" },
{ "$match": { "Array.field1": "a" } },
{ "$group": {
"_id": "$_id",
"total": { "$sum": 1 }
}}
])
De to første former er de "optimale" for moderne MongoDB-miljøer. Den endelige formular med $unwind
og $group
er en "legacy" konstruktion, som virkelig ikke har været nødvendig for denne type operation siden MongoDB 2.6, dog med nogle lidt forskellige operatører.
I de første to sammenligner vi grundlæggende field1
værdien af hvert array-element, mens det stadig er et array. Begge $filter
og $reduce
er moderne operatører designet til at arbejde med et eksisterende array på plads. Den samme sammenligning udføres på hver enkelt ved hjælp af aggregeringen $eq
operator som returnerer en boolesk værdi baseret på om de angivne argumenter er "lige" eller ej. I dette tilfælde på hvert array-medlem til den forventede værdi af "a"
.
I tilfælde af $filter
, forbliver arrayet faktisk intakt bortset fra alle elementer, der ikke opfyldte den leverede betingelse i "cond"
fjernes fra arrayet. Da vi stadig har et "array" som output, kan vi derefter bruge $størrelse
operatør for at måle antallet af array-elementer tilbage, efter at filterbetingelsen blev behandlet.
$reduce
på den anden side arbejder gennem array-elementerne og leverer et udtryk over hvert element og en lagret "akkumulator"-værdi, som vi initialiserede med "initialValue"
. I dette tilfælde den samme $eq
testen anvendes inden for $cond
operatør. Dette er en "ternær" eller if/then/else
betinget operator, som tillader et testet udtryk, som returnerer en boolesk værdi, for at returnere then
værdi, når sand
eller else
værdi når false
.
I det udtryk returnerer vi 1
eller 0
og angiv det overordnede resultat af at tilføje den returnerede værdi og den aktuelle "akkumulator" "$$værdi"
med $sum
operatør for at lægge disse sammen.
Den endelige formular brugte $unwind
på arrayet. Hvad dette faktisk gør, er at dekonstruere array-medlemmerne for at skabe et "nyt dokument" for hvert array-medlem og dets relaterede overordnede felter i det originale dokument. Dette "kopierer" effektivt hoveddokumentet for hvert array-medlem.
Når du $unwind
strukturen af dokumenterne ændres til en "fladere" form. Dette er grunden til, at du så kan lave den efterfølgende $match
pipeline for at fjerne de umatchede dokumenter.
Dette bringer os til $group
som anvendes til at "samle tilbage" al information relateret til en fælles nøgle. I dette tilfælde er det _id
felt i det originale dokument, som naturligvis blev kopieret ind i hvert dokument, der blev produceret af $unwind
. Når vi går tilbage til denne "fælles nøgle" som et enkelt dokument, kan vi "tælle" de resterende "dokumenter" udtrukket fra arrayet ved hjælp af $sum
akkumulator.
Hvis vi ville have det resterende "array" tilbage, så kan du $push
og genopbyg arrayet med kun de resterende medlemmer:
{ "$group": {
"_id": "$_id",
"Array": { "$push": "$Array" },
"total": { "$sum": 1 }
}}
Men selvfølgelig i stedet for at bruge $size
i en anden pipeline-fase kan vi simpelthen stadig "tælle", som vi allerede gjorde med $sum