Gruppieren nach Daten in Mongodb

Ich arbeite an einem Projekt, bei dem ich die Anzahl der Klicks auf ein Thema protokolliere.

Ich benutze mongodb und ich muss die Anzahl der Klicks nach Datum gruppieren (ich möchte Daten für 15 Tage gruppieren).

Ich habe Datenspeicher in folgendem Format in 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" } 

Ich möchte die Anzahl der Klicks auf Thema gruppieren: abc bis Tage (für 15 Tage) .. ich weiß, wie man das gruppiert, aber wie kann ich nach Datum gruppieren, die in meiner database gespeichert sind

Ich suche das Ergebnis im folgenden Format

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

Ich habe Code geschrieben, aber es funktioniert nur, wenn das Datum in String ist (Code ist hier http://pastebin.com/2wm1n1ix ) … bitte führe mich, wie ich es gruppiere

Solutions Collecting From Web of "Gruppieren nach Daten in Mongodb"

Neue Antwort mit Mongo Aggregation Framework

Nachdem diese Frage gestellt und beantwortet wurde, veröffentlichte 10gen Mongodb Version 2.2 mit einem Aggregations-Framework, das nun der bessere Weg ist, um diese Art von Abfrage durchzuführen. Diese Abfrage ist etwas schwierig, da Sie nach Datum gruppieren möchten und die gespeicherten Werte Zeitstempel sind. Sie müssen also etwas tun, um die Zeitstempel in übereinstimmende Daten zu konvertieren. Für die Zwecke des Beispiels werde ich nur eine Abfrage schreiben, die die richtigen zählt.

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

Dies wird etwas zurückgeben wie:

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

Sie müssen $match , um die Abfrage auf den Datumsbereich zu begrenzen, an dem Sie interessiert sind, und $project , um _id in date umzubenennen. Wie Sie den Tag des Jahres zurück in ein Datum umwandeln, bleibt dem Leser als Übung überlassen. 🙂

10gen hat ein praktisches SQL-zu-Mongo-Aggregation-Conversion-Diagramm, das einen Lesezeichen-Wert hat. Es gibt auch einen speziellen Artikel über Datumsaggregationsoperatoren .

Wenn du ein bisschen schicker wirst, kannst du folgendes benutzen:

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

Dadurch erhalten Sie die letzten 15 Tage und geben im Datumsfeld an jedem Tag ein date zurück. Beispielsweise:

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

Späte Antwort, aber für die Aufzeichnung (für jeden anderen, der auf diese Seite kommt): Sie müssen das “keyf” Argument anstelle von “key” verwenden, da Ihr Schlüssel tatsächlich eine function des Datums auf dem sein wird Ereignis (dh der “Tag” aus dem Datum extrahiert) und nicht das Datum selbst. Dies sollte tun, was Sie suchen:

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

Weitere Informationen finden Sie auf der MongoDB-Dokumentationsseite zu Aggregation und Group: http://www.mongodb.org/display/DOCS/Aggregation#Aggregation-Group

Dies kann helfen

 return new Promise(function(resolve, reject) { db.doc.aggregate( [ { $match: {} }, { $group: { _id: { $dateToString: { 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); } 

Ich habe noch nicht so viel mit MongoDB gearbeitet, also bin ich mir nicht ganz sicher. Können Sie Javascript nicht vollständig nutzen?
So können Sie Ihr Datum mit Javascript Date class analysieren, erstellen Sie Ihr Datum für den Tag daraus und legen Sie als Schlüssel in eine “out” -Eigenschaft. Und fügen Sie immer einen hinzu, wenn der Schlüssel bereits existiert, andernfalls erstellen Sie ihn neu mit Wert = 1 (erster Klick). Unten ist Ihr Code mit angepasster Reduce-function (ungetesteter Code!):

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

Eine weitere späte Antwort, aber immer noch. Wenn Sie also nur eine Iteration durchführen und die Anzahl der Klicks nach Datum und Thema gruppieren möchten, können Sie den folgenden Code verwenden:

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

Auch wenn Sie die Abfrage wie vorgeschlagen optimieren möchten, können Sie für das Datum anstelle der Zeichenfolge einen Integer-Wert für das Datum verwenden (Hinweis: use valueOf ()), obwohl die Geschwindigkeit für meine Beispiele gleich war.

Außerdem ist es immer ratsam, die MongoDB-Dokumente regelmäßig zu überprüfen, da sie ständig neue functionen hinzufügen. Zum Beispiel mit dem neuen Aggregation-Framework, das in der Version 2.2 veröffentlicht wird, können Sie die gleichen Ergebnisse viel einfacher erreichen http://docs.mongodb.org/manual/applications/aggregation/

Danke für @mindthief, deine Antwort hilft mein Problem heute zu lösen. Die unten stehende function kann tagsüber ein wenig einfacher gruppieren, Hoffnung kann anderen helfen.

 /** * 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'}) 

Wenn Sie möchten, dass ein Date-Objekt direkt zurückgegeben wird

Verwenden Sie dann anstelle der Datumsaggregationsoperatoren stattdessen “Date Math”, um das Datumsobjekt zu runden. Dies kann oft wünschenswert sein, da alle Treiber ein BSON-Datum in einer Form darstellen, die üblicherweise für die Datumsmanipulation in allen Sprachen verwendet wird, wo dies möglich ist:

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

Oder, wie in der Frage impliziert ist, dass das erforderliche Gruppierungsintervall “Buckets” von 15 Tagen ist, dann wenden Sie das einfach auf den numerischen Wert in $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 } }} ]) 

Die angewandte Grundmathematik ist, dass, wenn Sie zwei Date Objekte $subtract , das zurückgegebene Ergebnis die Millisekunden der Differenz numerisch ist. Die Epoche wird also von Date(0) als Basis für die Konvertierung in jedem Sprachkonstruktor dargestellt, den Sie haben.

Bei einem numerischen Wert wird der “modulo” ( $mod ) angewendet, um das Datum (den Rest von der Division zu subtrahieren) auf das erforderliche Intervall zu runden. Entweder:

1000 Millisekunden x 60 Sekunden * 60 Minuten * 24 Stunden = 1 Tag

Oder

1000 Millisekunden x 60 Sekunden * 60 Minuten * 24 Stunden * 15 Tage = 15 Tage

So ist es flexibel in jedem Intervall, das Sie benötigen.

Aus dem gleichen Grund liefert eine $add Operation zwischen einem “numerischen” Wert und einem Date Objekt ein Date Objekt, das dem Millsekunden-Wert beider Objekte kombiniert ist (Epoche ist 0, also 0 plus Differenz ist das konvertierte Datum).

In der folgenden Auflistung einfach dargestellt und reproduzierbar:

 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(); 

Und führen Sie das zweite Beispiel mit 15-tägigen Intervallen aus:

 { "_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 } 

Oder eine ähnliche Verteilung in Abhängigkeit vom aktuellen Datum, an dem die Listung ausgeführt wird, und natürlich sind die 15-Tages-Intervalle seit dem Epochendatum konsistent.

Die Verwendung der "Math" -Methode ist ein wenig einfacher abzustimmen, besonders wenn Sie Zeitperioden für verschiedene Zeitzonen in der Aggregationsausgabe einstellen möchten, wo Sie in ähnlicher Weise numerisch anpassen können, indem Sie die numerische Differenz von UTC addieren / subtrahieren.

Natürlich ist das eine gute Lösung. Abgesehen davon können Sie Daten nach Tagen als Strings gruppieren (wie diese Antwort vorschlagen) oder Sie können den Beginn von Daten durch Projektion des Datumsfeldes (in Aggregation) wie folgt erhalten:

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

Es gibt dir das:

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

Es hat einige Pluspunkte: Sie können mit Ihrem Tag-in-Datum-Typ (nicht Zahl oder Zeichenfolge) manipulieren, es ermöglicht Ihnen, alle Datumsaggregationsoperatoren in folgenden Aggregationsoperationen zu verwenden und gibt Ihnen einen Datumstyp für die Ausgabe.