Resultado do grupo por 15 minutos de intervalo de tempo no MongoDb

Eu tenho uma coleção de “status” como esta 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") } .... .... 

Eu preciso obter resultado agrupado por 15 minutos de intervalo a partir dessa coleção.

Há algumas maneiras de fazer isso.

A primeira é com os Operadores de Agregação de Data , que permitem dissecar os valores de “data” nos documentos. Especificamente para “agrupamento” como a intenção 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 } }} ]) 

A segunda maneira é usar um pequeno truque de quando um object de data é subtraído (ou outra operação matemática direta) de outro object de data, então o resultado é um valor numérico que representa os milissegundos de data e hora da época entre os dois objects. Então, apenas usando a data de época, você obtém a representação de milissegundos de época. Em seguida, use data matemática para o 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 } }} ]) 

Portanto, depende do tipo de formato de saída desejado para o intervalo de agrupamento. Ambos basicamente representam a mesma coisa e têm dados suficientes para reconstruir como um object de “data” no seu código.

Você pode colocar qualquer outra coisa que você quiser na parte “operador de agrupamento” após o _id agrupamento. Eu estou apenas usando o exemplo básico de “contagem” em vez de qualquer declaração real de si mesmo sobre o que você realmente quer fazer.


MongoDB 4.x e Upwards

Houve alguns acréscimos aos Operadores de Agregação de Data desde a escrita original, mas do MongoDB 4.0 haverá “conversão real de tipos” reais em oposição aos truques matemáticos básicos feitos aqui com a conversão de Data BSON.

Por exemplo, podemos usar $toLong e $toDate como novos ajudantes aqui:

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

Isso é um pouco mais curto e não requer a definição de uma data BSON externa para o valor “epoch” como uma constante na definição do pipeline, por isso é bastante consistente para todas as implementações de linguagem.

Esses são apenas dois dos methods “auxiliares” para a conversão de tipos, todos vinculados ao método $convert , que é uma forma “mais longa” da implementação, permitindo manipulação customizada em null ou erro na conversão.

É possível até mesmo com essa conversão obter as informações de Date do ObjectId da chave primária, pois isso seria uma fonte confiável de data de “criação”:

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

Portanto, “tipos de conversão” com esse tipo de conversão pode ser uma ferramenta bastante poderosa.

Eu gosto da outra resposta aqui, e principalmente pelo uso de data math em vez de operadores de datas de agregação que, embora úteis, também podem ser um pouco obscuros.

A única coisa que quero adicionar aqui é que você também pode retornar um object Date da estrutura de agregação por essa abordagem, em oposição ao registro de data e hora “numérico” como resultado. É apenas um pouco de matemática extra nos mesmos princípios, 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 } }} ]) 

As construções de Date(0) em JavaScript aqui representam a mesma data de “época” em uma forma mais curta, como 0 milissegundo de epoch é epoch. Mas o ponto principal é que quando a “adição” a outro object de data BSON é feita com um identificador numérico, então o inverso da condição descrita é verdadeiro e o resultado final é atualmente uma Date .

Todos os drivers retornarão o tipo de Date nativo para seu idioma por essa abordagem.

Um pouco mais bonito para o 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}} ]) 

Outra maneira útil:

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

E mais fácil para intervalos min , hora e dia :

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