【问题标题】:Efficiently count percentage of occurrence in MongoDB有效计算 MongoDB 中出现的百分比
【发布时间】:2011-01-26 22:56:36
【问题描述】:

所以,我正在修补 MongoDB,并且我正在尝试使 count() 聚合查询正确扩展,以便我轻松计算文档中某些值在集合中出现的百分比。

我有一个结构如下的文档:

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

现在,我有几千个这样的文档,我想有效地计算 values 对象中所有值的真百分比(或假百分比)(在我的例子中,大约有 50 )。即,alpha 为真、beta 为真等的时间百分比。

我天真地开始使用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 秒。

最好的方法是什么?

【问题讨论】:

    标签: php mongodb database


    【解决方案1】:

    这是一个简单的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 批处理作业来填充计数集合,然后使用 upsert 使其保持最新。

    【讨论】:

    • @msteam 绝对有使用 map-reduce 的正确想法。请注意,(通常)map-reduce 不打算用于“实时”查询。如果您只是计算正确,那么保留“计数”列是执行此操作的简单方法。但是,如果您想要更大或更复杂的“汇总”,通常的方法是运行预定的 map-reduce。
    • @Gates VP 哦,当然;这是针对预定进程,而不是针对用户请求。但是几十条记录的 9 秒意味着一天中有足够的时间来处理我需要的所有处理。
    猜你喜欢
    • 1970-01-01
    • 2011-06-01
    • 2021-01-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多