Ejecución de la statement de caso en el marco de agregación mongodb

Estoy evaluando qué tan bien el marco de agregación MongoDB se adapta a nuestras necesidades, ya que actualmente nos estamos ejecutando sobre SQL Server. Me está costando realizar una consulta específica:

Digamos que tengo los siguientes pseudo registros (modelados como columnas en una tabla sql y como un documento completo en una colección mongodb)

{ name: 'A', timespent: 100, }, { name: 'B', timespent: 200, }, { name: 'C', timespent: 300, }, { name: 'D', timespent: 400, }, { name: 'E', timespent: 500, } 

Quiero agrupar el campo timespent en rangos y contar las ocurrencias para obtener, por ejemplo, los siguientes pseudoregistros:

 results{ 0-250: 2, 250-450: 2, 450-650: 1 } 

Tenga en cuenta que estos rangos (250, 450 y 650) son dynamics y es probable que el usuario los modifique con el tiempo. En SQL extrajimos los resultados con algo como esto:

 select range, COUNT(*) as total from ( select case when Timespent <= 250 then '0-250' when Timespent <= 450 then '200-450' else '450-600' end as range from TestTable) as r group by r.range 

Una vez más, tenga en cuenta que este sql está construido dinámicamente por nuestra aplicación para adaptarse a los rangos específicos disponibles en cualquier momento.

Estoy luchando para encontrar las construcciones apropiadas en el marco de agregación de mongodb para realizar tales consultas. Puedo consultar los resultados de un solo rango insertando un $ match en la tubería (es decir, obteniendo el resultado de un rango único) pero no puedo entender cómo extraer todos los rangos y sus recuentos en una única consulta de interconexión.

lo que corresponde a la statement SQL “case” en el marco de agregación , es el operador $ cond (ver manual ). Las sentencias $ cond se pueden anidar para simular “cuando-luego” y “else”, pero he elegido otro enfoque, porque es más fácil de leer (y generar, ver a continuación): usaré el operador $ concat para escribir la cadena de rango, que luego sirve como clave de agrupación.

Entonces para la colección dada:

 db.xx.find() { "_id" : ObjectId("514919fb23700b41723f94dc"), "name" : "A", "timespent" : 100 } { "_id" : ObjectId("514919fb23700b41723f94dd"), "name" : "B", "timespent" : 200 } { "_id" : ObjectId("514919fb23700b41723f94de"), "name" : "C", "timespent" : 300 } { "_id" : ObjectId("514919fb23700b41723f94df"), "name" : "D", "timespent" : 400 } { "_id" : ObjectId("514919fb23700b41723f94e0"), "name" : "E", "timespent" : 500 } 

el agregado (codificado) se ve así:

 db.xx.aggregate([ { $project: { "_id": 0, "range": { $concat: [{ $cond: [ { $lte: ["$timespent", 250] }, "range 0-250", "" ] }, { $cond: [ { $and: [ { $gte: ["$timespent", 251] }, { $lt: ["$timespent", 450] } ] }, "range 251-450", "" ] }, { $cond: [ { $and: [ { $gte: ["$timespent", 451] }, { $lt: ["$timespent", 650] } ] }, "range 450-650", "" ] }] } }}, { $group: { _id: "$range", count: { $sum: 1 } } }, { $sort: { "_id": 1 } }, ]); 

y el resultado es:

 { "result" : [ { "_id" : "range 0-250", "count" : 2 }, { "_id" : "range 251-450", "count" : 2 }, { "_id" : "range 450-650", "count" : 1 } ], "ok" : 1 } 

Para generar el comando agregado, debe comstackr la proyección “rango” como un objeto JSON (o podría generar una cadena y luego usar JSON.parse (cadena))

El generador se ve así:

 var ranges = [ 0, 250, 450, 650 ]; var rangeProj = { "$concat": [] }; for (i = 1; i < ranges.length; i++) { rangeProj.$concat.push({ $cond: { if: { $and: [{ $gte: [ "$timespent", ranges[i-1] ] }, { $lt: [ "$timespent", ranges[i] ] }] }, then: "range " + ranges[i-1] + "-" + ranges[i], else: "" } }) } db.xx.aggregate([{ $project: { "_id": 0, "range": rangeProj } }, { $group: { _id: "$range", count: { $sum: 1 } } }, { $sort: { "_id": 1 } }]); 

que devolverá el mismo resultado que el anterior.

A partir de MongoDB 3.4 podemos usar el operador $switch para realizar una instrucción multi-switch en la etapa $project .

El operador de canalización de $group agrupa los documentos por “rango” y devuelve el “recuento” para cada grupo utilizando el operador de acumulador $sum .

 db.collection.aggregate( [ { "$project": { "range": { "$switch": { "branches": [ { "case": { "$lte": [ "$timespent", 250 ] }, "then": "0-250" }, { "case": { "$and": [ { "$gt": [ "$timespent", 250 ] }, { "$lte": [ "$timespent", 450 ] } ] }, "then": "251-450" }, { "case": { "$and": [ { "$gt": [ "$timespent", 450 ] }, { "$lte": [ "$timespent", 650 ] } ] }, "then": "451-650" } ], "default": "650+" } } }}, { "$group": { "_id": "$range", "count": { "$sum": 1 } }} ] ) 

Con los siguientes documentos en nuestra colección,

 { "_id" : ObjectId("514919fb23700b41723f94dc"), "name" : "A", "timespent" : 100 }, { "_id" : ObjectId("514919fb23700b41723f94dd"), "name" : "B", "timespent" : 200 }, { "_id" : ObjectId("514919fb23700b41723f94de"), "name" : "C", "timespent" : 300 }, { "_id" : ObjectId("514919fb23700b41723f94df"), "name" : "D", "timespent" : 400 }, { "_id" : ObjectId("514919fb23700b41723f94e0"), "name" : "E", "timespent" : 500 } 

nuestra consulta rinde:

 { "_id" : "451-650", "count" : 1 } { "_id" : "251-450", "count" : 2 } { "_id" : "0-250", "count" : 2 } 

Es posible que deseemos agregar una etapa $sort a la interconexión ordenando nuestro documento por rango, pero esto solo ordenará los documentos en orden lexicográfico debido al tipo de “rango”.