Stai iniziando a pensare lungo le linee giuste qui mentre eri diretto nella giusta direzione. Cambiando la tua mentalità SQL, "distinto" è davvero solo un altro modo di scrivere un $group
operazione in una delle due lingue. Ciò significa che ne hai due operazioni di gruppo che si svolgono qui e, in termini di pipeline di aggregazione, due fasi della pipeline.
Solo con documenti semplificati da visualizzare:
{
"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"
}
È logico che per la data combinazione di "campagna" il conteggio totale e il conteggio "distinto" siano rispettivamente "3" e "2". Quindi la cosa logica da fare è "raggruppare" prima tutti quei valori "subscriber_id" e mantenere il conteggio delle occorrenze per ciascuno, quindi mentre si pensa a "pipeline", "totale" quei conteggi per "campagna" e poi basta contare il " distinte" occorrenze come un numero separato:
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 }
}}
])
Dopo il primo "gruppo" i documenti di output possono essere visualizzati in questo modo:
{
"_id" : {
"campaign_id" : "A",
"campaign_name" : "A",
"subscriber_id" : "456"
},
"count" : 1
}
{
"_id" : {
"campaign_id" : "A",
"campaign_name" : "A",
"subscriber_id" : "123"
},
"count" : 2
}
Quindi dai "tre" documenti nel campione, "2" appartiene a un valore distinto e "1" a un altro. Questo può ancora essere sommato con $sum
al fine di ottenere il totale dei documenti di corrispondenza che farete nella fase successiva, con il risultato finale:
{
"_id" : {
"campaign_id" : "A",
"campaign_name" : "A"
},
"totalCount" : 3,
"distinctCount" : 2
}
Un'analogia davvero buona per la pipeline di aggregazione è la pipe unix "|" operatore, che consente il "concatenamento" delle operazioni in modo da poter passare l'output di un comando all'input del successivo e così via. Iniziare a pensare ai tuoi requisiti di elaborazione in questo modo ti aiuterà a comprendere meglio le operazioni con la pipeline di aggregazione.