web-dev-qa-db-fra.com

Sélectionnez Grouper par nombre et nombre distinct dans la même requête mongodb

J'essaye de faire quelque chose comme

select campaign_id,campaign_name,count(subscriber_id),count(distinct subscriber_id)
group by campaign_id,campaign_name from campaigns;

Cette requête donnant des résultats sauf count (distinct subscriber_id)

db.campaigns.aggregate([
    {$match: {subscriber_id: {$ne: null}}},
    {$group: { 
        _id: {campaign_id: "$campaign_id",campaign_name: "$campaign_name"},
        count: {$sum: 1}
    }}
])

Cette requête suivante donne des résultats sauf count (subscriber_id)

db.campaigns_logs.aggregate([
    {$match : {subscriber_id: {$ne: null}}},
    {$group : { _id: {campaign_id: "$campaign_id",campaign_name: "$campaign_name",subscriber_id: "$subscriber_id"}}},
    {$group : { _id: {campaign_id: "$campaign_id",campaign_name: "$campaign_name"}, 
                count: {$sum: 1}
              }}
])

mais je veux compter (subscriber_id), compter (distinct subscriber_id) dans le même résultat

22
Rams

Vous commencez à penser dans la bonne direction ici alors que vous vous dirigiez dans la bonne direction. Changer votre état d'esprit SQL, "distinct" est vraiment juste une autre façon d'écrire un $group opération dans les deux langues. Cela signifie que vous avez deux opérations de groupe qui se produisent ici et, en termes de pipeline d'agrégation, deux étapes de pipeline.

Juste avec des documents simplifiés pour visualiser:

{
    "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"
}

Il va de soi que pour la combinaison "campagne" donnée, le nombre total et le nombre "distinct" sont respectivement "3" et "2". Donc, la chose logique à faire est de "grouper" toutes ces valeurs "subscriber_id" en premier et de garder le nombre d'occurrences pour chacune, puis en pensant à "pipeline", "totaliser" ces nombres par "campagne" et ensuite simplement compter les " occurrences distinctes sous la forme d'un nombre distinct:

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 }
    }}
])

Après le premier "groupe", les documents de sortie peuvent être visualisés comme ceci:

{ 
    "_id" : { 
        "campaign_id" : "A", 
        "campaign_name" : "A", 
        "subscriber_id" : "456"
    }, 
    "count" : 1 
}
{ 
    "_id" : { 
        "campaign_id" : "A", 
        "campaign_name" : "A", 
        "subscriber_id" : "123"
    }, 
    "count" : 2
}

Ainsi, parmi les "trois" documents de l'échantillon, "2" appartient à une valeur distincte et "1" à une autre. Cela peut toujours être totalisé avec $sum afin d'obtenir le total des documents correspondants que vous faites à l'étape suivante, avec le résultat final:

{ 
    "_id" : { 
        "campaign_id" : "A", 
        "campaign_name" : "A"
    },
    "totalCount" : 3,
    "distinctCount" : 2
}

Une très bonne analogie pour le pipeline d'agrégation est le tube unix "|" , qui permet de "chaîner" les opérations afin que vous puissiez passer la sortie d'une commande à l'entrée de la suivante, et ainsi de suite. Commencer à penser à vos exigences de traitement de cette manière vous aidera à mieux comprendre les opérations avec le pipeline d'agrégation.

58
Neil Lunn

Requête SQL: (regrouper par & nombre de distincts)

select city,count(distinct(emailId)) from TransactionDetails group by city;

La requête mongo équivalente ressemblerait à ceci:

db.TransactionDetails.aggregate([ 
{$group:{_id:{"CITY" : "$cityName"},uniqueCount: {$addToSet: "$emailId"}}},
{$project:{"CITY":1,uniqueCustomerCount:{$size:"$uniqueCount"}} } 
]);
7
Surendranath Reddy K