sql >> Base de Datos >  >> NoSQL >> MongoDB

calcular la frecuencia usando el marco agregado de mongodb

Si solo se trata de obtener cosas en intervalos de 10 segundos, puede hacer un poco de matemática y ejecutar esto a través del agregado:

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

Eso descompone las cosas en intervalos de 10 segundos en un minuto donde ocurren con un poco de matemática mod 10.

Creo que es razonable y sería el corredor más rápido ya que usa agregados. Si realmente necesita que su secuencia, como se muestra, sea una ejecución de 10 segundos desde un tiempo inicialmente coincidente, puede realizar el proceso con mapReduce:

Primero un mapeador:

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

}

Entonces, esto emitirá fechas dentro de un intervalo de 10 segundos, comenzando con la primera fecha y luego aumentando el intervalo cada vez que se encuentre algo fuera de rango

Ahora necesitas un reductor:

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

Muy simple. Simplemente devuelva la longitud de la matriz pasada.

Debido a que mapReduce funciona de la forma en que lo hace, cualquier cosa que no tenga más de un valor no se pasa al reductor, así que limpie esto con finalizar:

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

Luego simplemente ejecútalo para obtener los resultados. Tenga en cuenta la sección "alcance" que pasa una variable global para ser utilizada en el mapeador:

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

Es probable que cada enfoque brinde resultados ligeramente diferentes, pero ese es el punto. Depende de cuál quieras usar realmente.

Teniendo en cuenta su comentario, podría "inspeccionar" el resultado de cualquiera de las declaraciones y "llenar los espacios" programáticamente, por así decirlo. Generalmente prefiero esa opción, pero no es mi programa y no sé qué tan grande es la serie que está tratando de recuperar de esta consulta.

En el lado del servidor, puede parchear el "mapeador" para hacer algo como esto:

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

}