Du börjar tänka i rätt riktning här eftersom du var på väg åt rätt håll. Att ändra ditt SQL-tänkesätt, "distinkt" är egentligen bara ett annat sätt att skriva en $group
drift på båda språken. Det betyder att du har två gruppverksamhet som sker här och, i aggregerade pipelinetermer, två pipelinesteg.
Bara med förenklade dokument att visualisera:
{
"campaign_id": "A",
"campaign_name": "A",
"subscriber_id": "123"
},
{
"campaign_id": "A",
"campaign_name": "A",
"subscriber_id": "123"
},
{
"campaign_id": "A",
"campaign_name": "A",
"subscriber_id": "456"
}
Det är naturligt att för den givna "kampanj"-kombinationen är det totala antalet och "distinkt" antalet "3" respektive "2". Så det logiska att göra är att "gruppera" alla dessa "subscriber_id"-värden först och behåll räkningen av förekomster för varje, sedan medan du tänker "pipeline", "totalt" dessa räkningar per "kampanj" och sedan bara räkna " distinkt" förekomster som ett separat nummer:
db.campaigns.aggregate([
{ "$match": { "subscriber_id": { "$ne": null }}},
// Count all occurrences
{ "$group": {
"_id": {
"campaign_id": "$campaign_id",
"campaign_name": "$campaign_name",
"subscriber_id": "$subscriber_id"
},
"count": { "$sum": 1 }
}},
// Sum all occurrences and count distinct
{ "$group": {
"_id": {
"campaign_id": "$_id.campaign_id",
"campaign_name": "$_id.campaign_name"
},
"totalCount": { "$sum": "$count" },
"distinctCount": { "$sum": 1 }
}}
])
Efter den första "gruppen" kan de utgående dokumenten visualiseras så här:
{
"_id" : {
"campaign_id" : "A",
"campaign_name" : "A",
"subscriber_id" : "456"
},
"count" : 1
}
{
"_id" : {
"campaign_id" : "A",
"campaign_name" : "A",
"subscriber_id" : "123"
},
"count" : 2
}
Så från de "tre" dokumenten i urvalet tillhör "2" ett distinkt värde och "1" till ett annat. Detta kan fortfarande summeras med $sum
för att få de totala matchande dokumenten som du gör i följande steg, med slutresultatet:
{
"_id" : {
"campaign_id" : "A",
"campaign_name" : "A"
},
"totalCount" : 3,
"distinctCount" : 2
}
En riktigt bra analogi för aggregationspipeline är unix-röret "|" operator, som tillåter "kedja" av operationer så att du kan skicka utdata från ett kommando till ingången för nästa, och så vidare. Att börja tänka på dina bearbetningskrav på det sättet kommer att hjälpa dig att förstå verksamheten med aggregeringspipelinen bättre.