sql >> Base de Datos >  >> NoSQL >> MongoDB

Seleccione Agrupar por conteo y conteo distinto en la misma consulta mongodb

Estás comenzando a pensar en las líneas correctas aquí, ya que te dirigías en la dirección correcta. Cambiando su mentalidad de SQL, "distinto" es realmente otra forma de escribir un $group funcionamiento en cualquiera de los dos idiomas. Eso significa que tienes dos operaciones grupales que ocurren aquí y, en términos de canalización de agregación, dos etapas de canalización.

Solo con documentos simplificados para visualizar:

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

Es lógico que para la combinación de "campaña" dada, el recuento total y el recuento "distinto" sean "3" y "2" respectivamente. Entonces, lo lógico es "agrupar" todos esos valores de "subscriber_id" primero y mantener el conteo de ocurrencias para cada uno, luego mientras piensa en "canalización", "total" esos conteos por "campaña" y luego simplemente cuente el " "distintas" ocurrencias como un número separado:

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

Después del primer "grupo", los documentos de salida se pueden visualizar así:

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

Entonces, de los "tres" documentos de la muestra, "2" pertenecen a un valor distinto y "1" a otro. Esto todavía se puede sumar con $sum para obtener el total de documentos coincidentes lo que haces en la siguiente etapa, con el resultado final:

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

Una muy buena analogía para la tubería de agregación es la tubería de Unix "|" operador, que permite el "encadenamiento" de operaciones para que pueda pasar la salida de un comando a la entrada del siguiente, y así sucesivamente. Comenzar a pensar en sus requisitos de procesamiento de esa manera lo ayudará a comprender mejor las operaciones con la canalización de agregación.