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

Berechnen Sie die Häufigkeit mit dem Aggregate-Framework von Mongodb

Wenn es nur darum geht, Dinge innerhalb von 10-Sekunden-Intervallen zu erhalten, können Sie ein wenig rechnen und dies durch Aggregat laufen lassen:

db.collection.aggregate([
    { "$group": {
        "_id": {
             "year": { "$year": "$created_at" },
             "month":{ "$month": "$created_at" },
             "day": { "$dayOfMonth": "$created_at" },
             "hour": { "$hour": "$created_at" },
             "minute": { "$minute": "$created_at" },
             "second": { "$subtract": [
                 { "$second": "$created_at" },
                 { "$mod": [
                     { "$second": "$created_at" },
                     10
                 ]}
             ]}
        },
        "count": { "$sum" : 1 }
    }}
])

Das bricht die Dinge also auf die Intervalle von 10 Sekunden in einer Minute herunter, wo sie mit ein wenig Mod 10-Mathematik auftreten.

Ich denke, das ist vernünftig und wäre der schnellste Läufer, da es Aggregate verwendet. Wenn Sie wirklich brauchen, dass Ihre Sequenz wie gezeigt 10 Sekunden von einer ursprünglich übereinstimmenden Zeit läuft, können Sie den Vorgang mit mapReduce:

durchführen

Zuerst ein Mapper:

var mapper = function () {

    if ( this.created_at.getTime() > ( last_date + 10000 ) ) {
        if ( last_date == 0 ) {
            last_date = this.created_at.getTime();
        } else {
            last_date += 10000;
        }
    }

    emit(
        {
            start: new Date( last_date ),
            end: new Date( last_date + 10000 )
        },
        this.created_at
    );

}

Dies wird also Daten innerhalb eines 10-Sekunden-Intervalls ausgeben, beginnend mit dem ersten Datum und dann das Intervall jedes Mal erhöhen, wenn etwas außerhalb des Bereichs gefunden wird

Jetzt brauchen Sie einen Reduzierer:

var reducer = function (key, values) {
    return values.length;
};

Sehr einfach. Geben Sie einfach die Länge des übergebenen Arrays zurück.

Da mapReduce so funktioniert, wie es funktioniert, wird alles, was nicht mehr als einen Wert hatte, nicht an den Reducer übergeben, also bereinigen Sie dies mit finalize:

var finalize = function (key, value) {
    if ( typeof(value) == "object" ) {
        value = 1;
    }
    return value;
};

Führen Sie es dann einfach aus, um die Ergebnisse zu erhalten. Beachten Sie den Abschnitt "Bereich", der eine globale Variable übergibt, die im Mapper verwendet werden soll:

db.collection.mapReduce(
    mapper,
    reducer,
    { 
        "out": { "inline": 1 }, 
        "scope": { "last_date": 0 }, 
        "finalize": finalize 
    }
)

Jeder Ansatz wird wahrscheinlich leicht unterschiedliche Ergebnisse liefern, aber das ist der Punkt. Es hängt davon ab, welche Sie tatsächlich verwenden möchten.

In Anbetracht Ihres Kommentars könnten Sie entweder die Ausgabe einer der beiden Anweisungen "untersuchen" und die Lücken sozusagen programmgesteuert "füllen". Im Allgemeinen bevorzuge ich diese Option, aber es ist nicht mein Programm und ich weiß nicht, wie groß die Serie ist, die Sie mit dieser Abfrage abzurufen versuchen.

Auf der Serverseite können Sie den "Mapper" patchen, um so etwas zu tun:

var mapper = function () {

    if ( this.created_at.getTime() > ( last_date + 10000 ) ) {

        if ( last_date == 0 ) {
            last_date = this.created_at.getTime();
        } else {
            // Patching for empty blocks
            var times = Math.floor( 
                 ( this.created_at.getTime() - last_date ) / 10000
            );

            if ( times > 1 ) {
                for ( var i=1; i < times; i++ ) {
                    last_date += 10000;
                    emit(
                        {
                            start: new Date( last_date ),
                            end: new Date( last_date + 10000 )
                        },
                        0
                    );
                }
            }
            // End patch
            last_date += 10000;
        }
    }

    emit(
        {
            start: new Date( last_date ),
            end: new Date( last_date + 10000 )
        },
        this.created_at
    );

}