MongoDB
 sql >> Datenbank >  >> NoSQL >> MongoDB

Mongo-Aggregation innerhalb von Zeitintervallen

Je nachdem, welches Ausgabeformat Ihren Anforderungen am besten entspricht, gibt es verschiedene Vorgehensweisen. Der wichtigste Hinweis ist, dass mit dem "Aggregation Framework" selbst können Sie nicht wirklich etwas "umgesetztes" als Datum zurückgeben, aber Sie können Werte erhalten, die leicht in ein Date rekonstruiert werden können Objekt bei der Verarbeitung von Ergebnissen in Ihrer API.

Der erste Ansatz besteht darin, die "Datumsaggregationsoperatoren" zu verwenden für das Aggregationsframework verfügbar:

db.collection.aggregate([
    { "$match": {
        "time": { "$gte": startDate, "$lt": endDate }
    }},
    { "$group": {
        "_id": {
            "year": { "$year": "$time" },
            "dayOfYear": { "$dayOfYear": "$time" },
            "hour": { "$hour": "$time" },
            "minute": {
                "$subtract": [
                    { "$minute": "$time" },
                    { "$mod": [ { "$minute": "$time" }, 10 ] }
                ]
            }
        },
        "count": { "$sum": 1 }
    }}
])

Was einen zusammengesetzten Schlüssel für _id zurückgibt die alle gewünschten Werte für ein "Datum" enthält. Wenn Sie alternativ immer nur innerhalb einer "Stunde" sind, verwenden Sie einfach den "Minuten"-Teil und berechnen Sie das tatsächliche Datum basierend auf dem startDate Ihrer Sortimentsauswahl.

Oder Sie können einfach "Datumsmathematik" verwenden, um die Millisekunden seit "Epoche" zu erhalten, die wiederum direkt einem Datumskonstruktor zugeführt werden können.

db.collection.aggregate([
    { "$match": {
        "time": { "$gte": startDate, "$lt": endDate }
    }},
    { "$group": {
        "_id": {
            "$subtract": [
               { "$subtract": [ "$time", new Date(0) ] },
               { "$mod": [
                   { "$subtract": [ "$time", new Date(0) ] },
                   1000 * 60 * 10
               ]}
            ]
        },
        "count": { "$sum": 1 }
    }}
])

In allen Fällen, was Sie nicht tun verwenden möchten, ist $project bevor Sie $group tatsächlich anwenden . Als „Pipeline-Stufe“ $project muss alle ausgewählten Dokumente "durchlaufen" und den Inhalt "transformieren".

Das braucht Zeit , und addiert sich zur Ausführungssumme der Abfrage. Sie können sich einfach bei der $group bewerben direkt wie gezeigt wurde.

Oder wenn Sie wirklich "rein" über ein Date sind Objekt ohne Nachbearbeitung zurückgegeben wird, können Sie immer "mapReduce" , da die JavaScript-Funktionen eigentlich eine Umformung als Datum zulassen, aber langsamer als das Aggregations-Framework und natürlich ohne Cursor-Antwort:

db.collection.mapReduce(
   function() {
       var date = new Date(
           this.time.valueOf() 
           - ( this.time.valueOf() % ( 1000 * 60 * 10 ) )
       );
       emit(date,1);
   },
   function(key,values) {
       return Array.sum(values);
   },
   { "out": { "inline": 1 } }
)

Am besten verwenden Sie jedoch die Aggregation, da die Umwandlung der Antwort recht einfach ist:

db.collection.aggregate([
    { "$match": {
        "time": { "$gte": startDate, "$lt": endDate }
    }},
    { "$group": {
        "_id": {
            "year": { "$year": "$time" },
            "dayOfYear": { "$dayOfYear": "$time" },
            "hour": { "$hour": "$time" },
            "minute": {
                "$subtract": [
                    { "$minute": "$time" },
                    { "$mod": [ { "$minute": "$time" }, 10 ] }
                ]
            }
        },
        "count": { "$sum": 1 }
    }}
]).forEach(function(doc) {
    doc._id = new Date(doc._id);
    printjson(doc);
})

Und dann haben Sie Ihre Intervallgruppierungsausgabe mit echtem Date Objekte.