86 votes

grouper par dates dans mongodb

Je travaille sur un projet dans lequel je dois suivre 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 sur 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 par jours (pour 15 jours). Je sais comment regrouper cela mais comment puis-je regrouper par date qui sont stockées dans ma base de données.

Je cherche le résultat dans le format suivant

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

J'ai écrit un code mais il ne fonctionne que si la date est dans une chaîne (le code est ici). http://pastebin.com/2wm1n1ix ) ... s'il vous plaît, guidez-moi comment le regrouper.

96voto

Old Pro Points 7826

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

Après que cette question ait été posée et qu'il y ait été répondu, 10gen a publié la version 2.2 de Mongodb avec un cadre d'agrégation, qui est maintenant la meilleure façon de faire ce genre de requête. Cette requête est un peu difficile parce que vous voulez regrouper par date et les valeurs stockées sont des timestamps, donc vous devez faire quelque chose pour convertir les timestamps en dates qui correspondent. Pour les besoins de l'exemple, je vais simplement écrire une requête qui obtient les bons comptes.

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

Cela renverra 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 pour renommer _id à date . La manière de reconvertir le jour de l'année en date est laissée à l'appréciation du lecteur. :-)

10gen dispose d'un outil pratique Tableau de conversion de SQL à Mongo Aggregation à ajouter aux favoris. Il existe également un article spécifique sur opérateurs d'agrégation de dates .

En devenant un peu plus sophistiqué, 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} }
])

qui vous donnera les 15 derniers jours et renverra une date pour chaque jour dans le fichier date champ. 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")
    }
]

0 votes

Sauf que cette méthode n'est pas vraiment efficace, puisqu'elle ne permet pas de regrouper les dates (l'OP voulait qu'elles soient regroupées en tranches de 15 jours).

4 votes

@Andrew : Cette réponse et toutes les autres réponses classent les clics par jours, en pensant que "grouper le nombre de clics sur le sujet:abc par jours(pour 15 jours)" signifie 15 cases, une pour chaque jour dans une plage de 15 jours.

0 votes

Merci. Les opérateurs d'agrégation de dates sont décrits ici : docs.mongodb.org/manual/reference/aggregation/operator-date

35voto

mindthief Points 2074

Réponse tardive, mais pour mémoire (pour toute autre personne qui viendrait sur cette page) : Vous devrez utiliser l'argument "keyf" au lieu de "key", car votre clé sera en fait une fonction de la date de l'événement (c'est-à-dire le "jour" extrait de la date) et non la date elle-même. Cela devrait faire ce que vous recherchez :

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

3 votes

Cela fonctionne très bien. Une petite variante consiste à arrondir la date à un jour complet et à utiliser le long pour le regroupement au lieu de la chaîne de caractères, ce qui a rendu mon instruction de regroupement environ 5 fois plus rapide.

0 votes

Comment puis-je exécuter ceci sur nodejs ? db.collection('keywords', function(err, collection) { collection.group({ keyf : function(doc) { return { "day" : doc.published.getFullYear() + "/" + (doc.published.getMonth()+1) + "/" + doc.published.getDate() } ; }, initial : {count:0}, reduce : function(obj, prev) { prev.count++ ; } },function(err, tw_today) { });}) ; Cela donne une erreur de fonction indéfinie

0 votes

code var keyf = function(doc) { return { "day" : doc.published.getFullYear() + "/" + (doc.published.getMonth()+1) + "/" + doc.published.getDate() }; } db.collection('keywords', function(err, collection) { collection.group( keyf,{}, {count:0}, function(obj, prev) { prev.count++; } ,function(err, tw_today) { }); }); et j'obtiens les résultats maintenant mais comment trier les résultats selon le jour ?

4voto

enricog Points 1653

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 pleinement Javascript ?
Vous pourriez donc analyser votre date avec Javascript Date créez votre date du jour à partir de celle-ci et définissez-la comme clé dans une propriété "out". Et ajoutez toujours un si la clé existe déjà, sinon créez-la nouvellement avec la valeur = 1 (premier clic). Ci-dessous votre code avec la fonction reduce 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"}
}
)

3voto

phnessu4 Points 71

Merci pour @mindthief, votre réponse a permis de résoudre mon problème aujourd'hui. La fonction ci-dessous permet de regrouper par jour un peu plus facilement, j'espère que cela pourra 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'})

2voto

golja Points 713

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 groupé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++; }
 })

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

En outre, il est toujours judicieux de consulter régulièrement la documentation de MongoDB, qui ajoute sans cesse de nouvelles fonctionnalités. Par exemple, avec le nouveau cadre Aggregation, 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/

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X