MongoDB
 sql >> Base de Dados >  >> NoSQL >> MongoDB

calcular a frequência usando a estrutura agregada do mongodb


Se for apenas obter as coisas em intervalos de 10 segundos, você pode fazer um pouco de matemática e executar isso através do 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 }
    }}
])

Então, isso divide as coisas em intervalos de 10 segundos em um minuto, onde ocorrem com um pouco de matemática mod 10.

Eu acho que é razoável, e seria o corredor mais rápido, pois usa agregado. Se você realmente precisar que sua sequência, conforme mostrado, seja uma execução de 10 segundos a partir de um tempo inicialmente combinado, você pode fazer o processo com mapReduce:

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

}

Então isso vai emitir datas dentro de um intervalo de 10 segundos, começando com a primeira data e aumentando o intervalo cada vez que algo for encontrado fora do intervalo

Agora você precisa de um redutor:
var reducer = function (key, values) {
    return values.length;
};

Muito simples. Apenas retorne o comprimento do array passado.

Como mapReduce funciona do jeito que funciona, qualquer coisa que não tenha mais de um valor não é passada para o redutor, então limpe isso com finalize:
var finalize = function (key, value) {
    if ( typeof(value) == "object" ) {
        value = 1;
    }
    return value;
};

Em seguida, basta executá-lo para obter os resultados. Observe a seção "scope" que passa uma variável global a ser usada no mapeador:
db.collection.mapReduce(
    mapper,
    reducer,
    { 
        "out": { "inline": 1 }, 
        "scope": { "last_date": 0 }, 
        "finalize": finalize 
    }
)

Cada abordagem provavelmente dará resultados ligeiramente diferentes, mas esse é o ponto. Depende de qual você realmente quer usar.

Considerando seu comentário, você pode "inspecionar" a saída de qualquer instrução e "preencher as lacunas" programaticamente. Eu geralmente prefiro essa opção, mas não é o meu programa e não sei o tamanho da série que você está tentando recuperar dessa consulta.

No lado do servidor, você pode corrigir o "mapeador" para fazer algo como isto:
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
    );

}