MongoDB
 sql >> Database >  >> NoSQL >> MongoDB

calcolare la frequenza utilizzando il framework aggregato mongodb

Se si tratta solo di ottenere le cose entro intervalli di 10 secondi, puoi fare un po' di matematica ed eseguire questo in modo aggregato:

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

In modo che scomponga le cose agli intervalli di 10 secondi in un minuto in cui si verificano con un po' di matematica mod 10.

Penso che sia ragionevole e sarebbe il corridore più veloce poiché utilizza l'aggregato. Se hai davvero bisogno che la tua sequenza come mostrato sia a 10 secondi da un tempo inizialmente abbinato, puoi eseguire il processo con mapReduce:

Innanzitutto un mappatore:

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

}

Quindi questo emetterà date entro un intervallo di 10 secondi, a partire dalla prima data e quindi aumentando l'intervallo ogni volta che viene trovato qualcosa fuori dall'intervallo

Ora ti serve un riduttore:

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

Molto semplice. Restituisci semplicemente la lunghezza dell'array passato.

Poiché mapReduce funziona come funziona, tutto ciò che non ha più di un valore non viene passato al riduttore, quindi puliscilo con finalize:

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

Quindi eseguilo per ottenere i risultati. Nota la sezione "ambito" che passa una variabile globale da utilizzare nel mapper:

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

È probabile che ogni approccio dia risultati leggermente diversi, ma questo è il punto. Dipende da quale vuoi effettivamente utilizzare.

Considerando il tuo commento, potresti "ispezionare" l'output di entrambe le affermazioni e "riempire le lacune" in modo programmatico per così dire. In genere preferisco questa opzione, ma non è il mio programma e non so quanto sia grande una serie che stai cercando di recuperare da questa query.

Sul lato server, puoi rattoppare il "mapper" per fare qualcosa del genere:

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

}