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

MongoDB agrega la suma de cada clave en un subdocumento

Como se indicó, el procesamiento de documentos como este no es posible con el marco de agregación a menos que realmente proporcione todas las claves, como:

db.events.aggregate([
   { "$group": {
       "_id": "$app_id",
       "event_count": { "$sum": "$event_count" },
       "0": { "$sum": "$event_count_per_type.0" },
       "10": { "$sum": "$event_count_per_type.10" }
       "20": { "$sum": "$event_count_per_type.20" }
       "30": { "$sum": "$event_count_per_type.30" }
   }}
])

Pero, por supuesto, debe especificar explícitamente cada clave en la que desea trabajar. Esto es cierto tanto para el marco de agregación como para las operaciones generales de consulta en MongoDB, ya que para acceder a los elementos anotados en este formulario de "subdocumento", debe especificar la "ruta exacta" al elemento para poder hacer cualquier cosa con él.

El marco de agregación y las consultas generales no tienen el concepto de "recorrido", lo que significa que no pueden procesar "cada clave" de un documento. Eso requiere una construcción de lenguaje para hacer lo que no se proporciona en estas interfaces.

Sin embargo, en términos generales, usar un "nombre clave" como un punto de datos donde su nombre realmente representa un "valor" es un poco como un "anti-patrón". Una mejor manera de modelar esto sería usar una matriz y representar su "tipo" como un valor por sí mismo:

{
    "app_id": "DHJFK67JDSJjdasj909",
    "date: ISODate("2014-08-07T00:00:00.000Z"),
    "event_count": 32423,
    "events": [
        { "type": 0,  "value": 322  },
        { "type": 10, "value": 4234 },
        { "type": 20, "value": 653  },
        { "type": 30, "value": 7562 }
    ]
}

También tenga en cuenta que la "fecha" ahora es un objeto de fecha adecuado en lugar de una cadena, que también es una buena práctica. Sin embargo, este tipo de datos es fácil de procesar con el marco de agregación:

db.events.aggregate([
    { "$unwind": "$events" },
    { "$group": {
        "_id": { 
            "app_id": "$app_id",
            "type": "$events.type"
        },
        "event_count": { "$sum": "$event_count" },
        "value": { "$sum": "$value" }
    }},
    { "$group": {
        "_id": "$_id.app_id",
        "event_count": { "$sum": "$event_count" },
        "events": { "$push": { "type": "$_id.type", "value": "$value" } }
    }}
]) 

Eso muestra una agrupación de dos etapas que primero obtiene los totales por "tipo" sin especificar cada "clave" ya que ya no es necesario, luego regresa como un solo documento por "app_id" con los resultados en una matriz tal como se almacenaron originalmente. Este formulario de datos es generalmente mucho más flexible para ver ciertos "tipos" o incluso los "valores" dentro de un cierto rango.

Donde no puede cambiar la estructura, su única opción es mapReduce. Esto le permite "codificar" el recorrido de las claves, pero dado que esto requiere la interpretación y ejecución de JavaScript, no es tan rápido como el marco de agregación:

db.events.mapReduce(
    function() {
        emit(
            this.app_id,
            {
                "event_count": this.event_count,
                "event_count_per_type": this.event_count_per_type
            }
        );
    },
    function(key,values) {

        var reduced = { "event_count": 0, "event_count_per_type": {} };

        values.forEach(function(value) {
            for ( var k in value.event_count_per_type ) {
                if ( !redcuced.event_count_per_type.hasOwnProperty(k) )
                    reduced.event_count_per_type[k] = 0;
                reduced.event_count_per_type += value.event_count_per_type;
            }
            reduced.event_count += value.event_count;
        })
    },
    {
        "out": { "inline": 1 }
    }
)

Eso esencialmente atravesará y combinará las "claves" y sumará los valores de cada uno encontrado.

Así que tus opciones son:

  1. Cambie la estructura y trabaje con consultas estándar y agregación.
  2. Manténgase en la estructura y requiera el procesamiento de JavaScript y mapReduce.

Depende de sus necesidades reales, pero en la mayoría de los casos, la reestructuración produce beneficios.