Agrupe el resultado por intervalo de tiempo de 15 minutos en MongoDb

Tengo una colección de “estado” como este strcture –

{ _id: ObjectId("545a0b63b03dbcd1238b4567"), status: 1004, comment: "Rem dolor ipsam placeat omnis non. Aspernatur nobis qui nisi similique.", created_at: ISODate("2014-11-05T11:34:59.804Z") }, { _id: ObjectId("545a0b66b03dbcd1238b4568"), status: 1001, comment: "Sint et eos vero ipsa voluptatem harum. Hic unde voluptatibus et blanditiis quod modi.", created_at: ISODate("2014-11-05T11:35:02.814Z") } .... .... 

Necesito obtener un resultado agrupado por un intervalo de 15 minutos de esa colección.

Hay un par de maneras de hacer esto.

El primero es con los Operadores de agregación de fechas , que le permiten analizar los valores de “fecha” en los documentos. Específicamente para “agrupar” como la intención principal:

 db.collection.aggregate([ { "$group": { "_id": { "year": { "$year": "$created_at" }, "dayOfYear": { "$dayOfYear": "$created_at" }, "hour": { "$hour": "$created_at" }, "interval": { "$subtract": [ { "$minute": "$created_at" }, { "$mod": [{ "$minute": "$created_at"}, 15] } ] } }}, "count": { "$sum": 1 } }} ]) 

La segunda forma es mediante el uso de un pequeño truco de cuando se resta un objeto de fecha (u otra operación matemática directa) desde otro objeto de fecha, luego el resultado es un valor numérico que representa los milisegundos de fecha y hora entre los dos objetos. Entonces, solo usando la fecha de época, obtienes la representación de los milisegundos de época. Luego use la fecha matemática para el intervalo:

 db.collection.aggregate([ { "$group": { "_id": { "$subtract": [ { "$subtract": [ "$created_at", new Date("1970-01-01") ] }, { "$mod": [ { "$subtract": [ "$created_at", new Date("1970-01-01") ] }, 1000 * 60 * 15 ]} ] }, "count": { "$sum": 1 } }} ]) 

Por lo tanto, depende del tipo de formato de salida que desee para el intervalo de agrupación. Ambos representan básicamente lo mismo y tienen datos suficientes para volver a construir como un objeto “fecha” en su código.

Puede poner cualquier otra cosa que desee en la parte “operador de agrupamiento” después de agrupar _id . Solo estoy usando el ejemplo básico de “contar” en lugar de cualquier afirmación real sobre lo que realmente quieres hacer.


MongoDB 4.xy superior

Hubo algunas adiciones a los Operadores de Agregación de Fecha desde la escritura original, pero de MongoDB 4.0 habrá un verdadero “casting real de tipos” en oposición a los trucos matemáticos básicos hechos aquí con la conversión BSON Date.

Por ejemplo, podemos usar $toLong y $toDate como nuevos ayudantes aquí:

 db.collection.aggregate([ { "$group": { "_id": { "$toDate": { "$subtract": [ { "$toLong": "$created_at" }, { "$mod": [ { "$toLong": "$created_at" }, 1000 * 60 * 15 ] } ] } }, "count": { "$sum": 1 } }} ]) 

Eso es un poco más corto y no requiere definir una fecha BSON externa para el valor “época” como una constante en la definición de la tubería, por lo que es bastante consistente para todas las implementaciones de idiomas.

Esos son solo dos de los métodos de “ayuda” para la conversión de tipo que se relacionan con el método $convert , que es una forma “más larga” de la implementación que permite el manejo personalizado en caso de error o null en la conversión.

Incluso es posible con dicha conversión obtener la información de Date del ObjectId de la clave principal, ya que sería una fuente confiable de fecha de “creación”:

 db.collection.aggregate([ { "$group": { "_id": { "$toDate": { "$subtract": [ { "$toLong": { "$toDate": "$_id" } }, { "$mod": [ { "$toLong": { "$toDate": "_id" } }, 1000 * 60 * 15 ] } ] } }, "count": { "$sum": 1 } }} ]) 

Entonces, “tipos de conversión” con este tipo de conversión puede ser una herramienta bastante poderosa.

Me gusta la otra respuesta aquí, y sobre todo para el uso de las matemáticas de fecha en lugar de los operadores de fecha de agregación, que aunque útiles también pueden ser un poco oscuros.

Lo único que quiero agregar aquí es que también puede devolver un objeto Date desde el marco de agregación por este enfoque en lugar de la marca de tiempo “numérica” ​​como resultado. Es solo un poco de matemática extra en los mismos principios, usando $add :

 db.collection.aggregate([ { "$group": { "_id": { "$add": [ { "$subtract": [ { "$subtract": [ "$current_date", new Date(0) ] }, { "$mod": [ { "$subtract": [ "$current_date", new Date(0) ] }, 1000 * 60 * 15 ]} ] }, new Date(0) ] }, "count": { "$sum": 1 } }} ]) 

Las construcciones de Date(0) en JavaScript aquí representan la misma fecha de “época” en una forma más corta, ya que 0 milisegundos de época es época. Pero el punto principal es que cuando la “adición” a otro objeto de fecha BSON se hace con un identificador numérico, entonces el inverso de la condición descrita es verdadero y el resultado final es en realidad ahora una Date .

Todos los conductores devolverán el tipo de Date nativo a su idioma con este enfoque.

Un poco más hermoso para mongo db.version () <3.0

 db.collection.aggregate([ {$match: {created_at:{$exists:1}}}, {$group: { _id: {$add:[ {$dayOfYear: "$created_at" }, {$multiply: [{$year: "$created_at"}, 1000]} ]}, count: {$sum: 1 } }}, {$sort:{_id:-1}} ]) 

Otra forma útil:

 db.collection.aggregate([ {$group: { _id: { overallTime: { $dateToString: { format: "%Y-%m-%dT%H", date: "$created_at" } }, interval: { $trunc: { $divide: [{ $minute: "$created_at" }, 15 ]}} }, }}, ]) 

Y más fácil para intervalos de minutos , horas y días :

 var format = "%Y-%m-%dT%H:%M"; // 1 min var format = "%Y-%m-%dT%H"; // 1 hour var format = "%Y-%m-%d"; // 1 day db.collection.aggregate([ {$group: { _id: { $dateToString: { format: format, date: "$created_at" } }, }}, ])