grouper par dates dans mongodb

Je travaille sur un projet dans lequel je surveille le nombre de clics sur un sujet.

J’utilise mongodb et je dois regrouper le nombre de clics par date (je veux regrouper les données pendant 15 jours).

J’ai un magasin de données au format suivant dans mongodb

{ "_id" : ObjectId("4d663451d1e7242c4b68e000"), "date" : "Mon Dec 27 2010 18:51:22 GMT+0000 (UTC)", "topic" : "abc", "time" : "18:51:22" } { "_id" : ObjectId("4d6634514cb5cb2c4b69e000"), "date" : "Mon Dec 27 2010 18:51:23 GMT+0000 (UTC)", "topic" : "bce", "time" : "18:51:23" } 

Je veux regrouper le nombre de clics sur le sujet: abc by days (pendant 15 jours) .. Je sais comment regrouper mais comment puis-je grouper par date qui sont stockées dans ma firebase database

Je cherche le résultat dans le format suivant

 [ { "date" : "date in log", "click" : 9 }, { "date" : "date in log", "click" : 19 }, ] 

J’ai écrit du code mais cela ne fonctionnera que si la date est en chaîne (le code est ici http://pastebin.com/2wm1n1ix ) … s’il vous plaît guidez-moi comment le grouper

Nouvelle réponse utilisant le cadre d’agrégation Mongo

Une fois que cette question a été posée et répondue, 10gen a publié la version 2.2 de Mongodb avec une structure d’agrégation, qui est maintenant la meilleure façon de faire ce type de requête. Cette requête est un peu difficile car vous voulez grouper par date et les valeurs stockées sont des horodatages, vous devez donc faire quelque chose pour convertir les horodatages en dates correspondantes. Pour les besoins de l’exemple, je vais simplement écrire une requête qui obtient les comptes appropriés.

 db.col.aggregate( { $group: { _id: { $dayOfYear: "$date"}, click: { $sum: 1 } } } ) 

Cela retournera quelque chose comme:

 [ { "_id" : 144, "click" : 165 }, { "_id" : 275, "click" : 12 } ] 

Vous devez utiliser $match pour limiter la requête à la plage de dates qui vous intéresse et $project à renommer _id à date . La façon dont vous convertissez le jour de l’année en date est laissée à titre d’exercice pour le lecteur. 🙂

10gen a un tableau de conversion SQL à Mongo Aggregation très pratique qui vaut la peine d’être bookmarqué. Il existe également un article spécifique sur les opérateurs d’agrégation de dates .

Obtenir un peu plus d’amateur, vous pouvez utiliser:

 db.col.aggregate([ { $group: { _id: { $add: [ { $dayOfYear: "$date"}, { $multiply: [400, {$year: "$date"}] } ]}, click: { $sum: 1 }, first: {$min: "$date"} } }, { $sort: {_id: -1} }, { $limit: 15 }, { $project: { date: "$first", click: 1, _id: 0} } ]) 

ce qui vous donnera les 15 derniers jours et retournera un certain nombre de dates / heures au sein de chaque jour dans le champ de date . Par exemple:

 [ { "click" : 431, "date" : ISODate("2013-05-11T02:33:45.526Z") }, { "click" : 702, "date" : ISODate("2013-05-08T02:11:00.503Z") }, ... { "click" : 814, "date" : ISODate("2013-04-25T00:41:45.046Z") } ] 

Réponse tardive, mais pour le compte rendu (pour toute autre personne qui vient sur cette page): Vous devrez utiliser l’argument ‘keyf’ au lieu de ‘key’, car votre clé sera en fait fonction de la date du événement (c’est-à-dire le “jour” extrait de la date) et non la date elle-même. Cela devrait faire ce que vous cherchez:

 db.coll.group( { keyf: function(doc) { var date = new Date(doc.date); var dateKey = (date.getMonth()+1)+"/"+date.getDate()+"/"+date.getFullYear()+''; return {'day':dateKey}; }, cond: {topic:"abc"}, initial: {count:0}, reduce: function(obj, prev) {prev.count++;} }); 

Pour plus d’informations, consultez la page de documentation de MongoDB sur l’agrégation et le groupe: http://www.mongodb.org/display/DOCS/Aggregation#Aggregation-Group

Cela peut aider

 return new Promise(function(resolve, reject) { db.doc.aggregate( [ { $match: {} }, { $group: { _id: { $dateToSsortingng: { format: "%Y-%m-%d", date: "$date" } }, count: { $sum: 1 } } }, { $sort: { _id: 1 } } ] ).then(doc => { /* if you need a date object */ doc.forEach(function(value, index) { doc[index]._id = new Date(value._id); }, this); resolve(doc); }).catch(reject); } 

Je n’ai pas encore beaucoup travaillé avec MongoDB, donc je ne suis pas tout à fait sûr. Mais n’êtes-vous pas en mesure d’utiliser le Javascript complet?
Ainsi, vous pouvez parsingr votre date avec la classe de Date Javascript, créer votre date pour la journée et la définir comme clé dans une propriété “out”. Et ajoutez-en toujours un si la clé existe déjà, sinon créez-le avec la valeur = 1 (premier clic). Ci-dessous est votre code avec une fonction de réduction adaptée (code non testé!):

 db.coll.group( { key:{'date':true}, initial: {retVal: {}}, reduce: function(doc, prev){ var date = new Date(doc.date); var dateKey = date.getFullYear()+''+date.getMonth()+''+date.getDate(); (typeof prev.retVal[dateKey] != 'undefined') ? prev.retVal[dateKey] += 1 : prev.retVal[dateKey] = 1; }, cond: {topic:"abc"} } ) 

Une autre réponse tardive, mais quand même. Donc, si vous voulez le faire en une seule itération et obtenir le nombre de clics regroupés par date et par sujet, vous pouvez utiliser le code suivant:

 db.coll.group( { $keyf : function(doc) { return { "date" : doc.date.getDate()+"/"+doc.date.getMonth()+"/"+doc.date.getFullYear(), "topic": doc.topic }; }, initial: {count:0}, reduce: function(obj, prev) { prev.count++; } }) 

Aussi, si vous souhaitez optimiser la requête comme suggéré, vous pouvez utiliser une valeur entière pour date (indice: utilisez valueOf (), pour la date clé au lieu de la chaîne, bien que pour mes exemples la vitesse était la même.

De plus, il est toujours sage de consulter régulièrement les documents MongoDB, car ils continuent à append de nouvelles fonctionnalités tout le temps. Par exemple, avec le nouveau cadre d’agrégation, qui sera publié dans la version 2.2, vous pouvez obtenir les mêmes résultats beaucoup plus facilement http://docs.mongodb.org/manual/applications/aggregation/

merci pour @mindthief, votre réponse aide à résoudre mon problème aujourd’hui. La fonction ci-dessous peut se regrouper par jour un peu plus facilement, l’espoir peut aider les autres.

 /** * group by day * @param query document {key1:123,key2:456} */ var count_by_day = function(query){ return db.action.group( { keyf: function(doc) { var date = new Date(doc.time); var dateKey = (date.getMonth()+1)+"/"+date.getDate()+"/"+date.getFullYear(); return {'date': dateKey}; }, cond:query, initial: {count:0}, reduce: function(obj, prev) { prev.count++; } }); } count_by_day({this:'is',the:'query'}) 

Si vous souhaitez un object Date renvoyé directement

Puis, au lieu d’appliquer les opérateurs d’agrégation de dates , appliquez plutôt «date math» à l’object date. Cela peut souvent être souhaitable car tous les pilotes représentent une date BSON sous une forme couramment utilisée pour la manipulation de dates pour toutes les langues où cela est possible:

 db.datetest.aggregate([ { "$group": { "_id": { "$add": [ { "$subtract": [ { "$subtract": [ "$date", new Date(0) ] }, { "$mod": [ { "$subtract": [ "$date", new Date(0) ] }, 1000 * 60 * 60 * 24 ]} ]}, new Date(0) ] }, "click": { "$sum": 1 } }} ]) 

Ou si, comme cela est implicite dans la question, l’intervalle de regroupement requirejs est “buckets” de 15 jours, appliquez simplement cela à la valeur numérique de $mod :

 db.datetest.aggregate([ { "$group": { "_id": { "$add": [ { "$subtract": [ { "$subtract": [ "$date", new Date(0) ] }, { "$mod": [ { "$subtract": [ "$date", new Date(0) ] }, 1000 * 60 * 60 * 24 * 15 ]} ]}, new Date(0) ] }, "click": { "$sum": 1 } }} ]) 

Le calcul de base appliqué est que lorsque vous $subtract deux objects Date , le résultat renvoyé sera les millisecondes de differnce numériquement. Donc, epoch est représenté par Date(0) comme base de conversion dans n’importe quel constructeur de langage.

Avec une valeur numérique, le “modulo” ( $mod ) est appliqué pour arrondir la date (soustraire le rest de la division) à l’intervalle requirejs. Être soit:

1000 millisecondes x 60 secondes * 60 minutes * 24 heures = 1 jour

Ou

1000 millisecondes x 60 secondes * 60 minutes * 24 heures * 15 jours = 15 jours

Il est donc flexible à n’importe quel intervalle dont vous avez besoin.

De même, une opération $add entre une valeur “numérique” et un object Date renverra un object Date équivalent à la valeur millsecondes des deux objects combinés (epoch est 0, donc 0 plus la différence est la date convertie).

Facilement représenté et reproductible dans la liste suivante:

 var now = new Date(); var bulk = db.datetest.initializeOrderedBulkOp(); for ( var x = 0; x < 60; x++ ) { bulk.insert({ "date": new Date( now.valueOf() + ( 1000 * 60 * 60 * 24 * x ))}); } bulk.execute(); 

Et exécuter le deuxième exemple avec des intervalles de 15 jours:

 { "_id" : ISODate("2016-04-14T00:00:00Z"), "click" : 12 } { "_id" : ISODate("2016-03-30T00:00:00Z"), "click" : 15 } { "_id" : ISODate("2016-03-15T00:00:00Z"), "click" : 15 } { "_id" : ISODate("2016-02-29T00:00:00Z"), "click" : 15 } { "_id" : ISODate("2016-02-14T00:00:00Z"), "click" : 3 } 

Ou dissortingbution similaire en fonction de la date actuelle à laquelle la liste est exécutée et, bien sûr, les intervalles de 15 jours seront cohérents depuis la date de l’époque.

L'utilisation de la méthode "Math" est un peu plus facile à régler, en particulier si vous souhaitez ajuster les périodes pour différents fuseaux horaires dans la sortie d'agrégation.

Bien sûr, c’est une bonne solution. En dehors de cela, vous pouvez regrouper les dates par jours sous forme de chaînes (comme le propose cette réponse ) ou vous pouvez obtenir le début des dates en projetant le champ de date (en agrégation) comme ceci:

 {'$project': { 'start_of_day': {'$subtract': [ '$date', {'$add': [ {'$multiply': [{'$hour': '$date'}, 3600000]}, {'$multiply': [{'$minute': '$date'}, 60000]}, {'$multiply': [{'$second': '$date'}, 1000]}, {'$millisecond': '$date'} ]} ]}, }} 

Cela vous donne ceci:

 { "start_of_day" : ISODate("2015-12-03T00:00:00.000Z") }, { "start_of_day" : ISODate("2015-12-04T00:00:00.000Z") } 

Il a quelques avantages: vous pouvez manipuler avec vos jours dans le type de date (pas de nombre ou de chaîne), cela vous permet d’utiliser tous les opérateurs d’agrégation de dates dans les opérations d’agrégation suivantes et vous donne le type de date sur la sortie.