Эффективно посчитать процент встречаемости в MongoDB - PullRequest
3 голосов
/ 27 января 2011

Итак, я работаю с MongoDB и пытаюсь получить агрегацию count(), которая правильно масштабируется, чтобы позволить мне легко рассчитать процент появления определенных значений в документе в коллекции.

У меня есть документ со структурой, такой как:

{
    foo : 'bar',
    moo : 'cow',
    values : {
        alpha : true,
        beta : false,
        gamma : false,
        delta : true ... (many more)
    }
}

Теперь у меня есть несколько тысяч таких документов, и я хочу эффективно рассчитать процент истинного (или процент ложного) всех значений в объекте values (а в моем случае их ~ 50).т. е. какой процент времени альфа верна, бета верна и т. д.

Я начал наивно с count(), но кажется, что он допускает только один запрос за раз, поэтому я и сделалэто (с использованием класса PHP Mongo, но в основном это обычная функция count():

 $array_of_keys = array('alpha', 'beta', 'gamma', 'delta'...);
 for($i=0;$i<count($array_of_keys);$i++){
    $array_of_keys = [...]
    for($i=0;$i<count($array_of_keys);$i++){

$false  = intval($collection->count(array($array_of_keys[$i]=>false)));
$true  = intval($collection->count(array($array_of_keys[$i]=>true)));
}

Но даже при очень небольшом количестве записей (около 100) это заняло 9 секунд.

Какой лучший подход для этого?

1 Ответ

5 голосов
/ 27 января 2011

Вот простой MapReduce , который будет делать то, что вы хотите:

map = function() {
    for (var key in this.values){
        emit(key, {count:1, trues: (this.values[key] ? 1 : 0)});
    }
}

reduce = function(key, values){
    var out = values[0];
    for (var i=1; i < values.length; i++){
        out.count += values[i].count;
        out.trues += values[i].trues;
    }
    return out;
}

finalize = function(key, value){
    value.ratio = value.trues / value.count;
    return value;
}

db.runCommand({mapReduce:'collection',
               map:map,
               reduce:reduce,
               finalize:finalize,
               out:'counts'
               })

db.counts.findOne({_id:'alpha'})
{_id: 'alpha', value: {count: 100, trues: 52, ratio: 0.52}}

Вы также можете сделать upsert , например, когда вы вставите в свой основнойколлекция, которая даст вам в режиме реального времени просматривать ваши данные:

for (var key in this.values){
    db.counts.update({_id:key},
                     {$inc:{count:1, trues: (this.values[key] ? 1 : 0)}},
                     true);
}

На самом деле, вы даже можете объединить эти методы.Выполните одноразовое пакетное задание MapReduce, чтобы заполнить коллекцию подсчетов, а затем используйте upserts, чтобы поддерживать его в актуальном состоянии.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...