【问题标题】:How do I not save data in my reduce() function in MongoDB?如何不在 MongoDB 的 reduce() 函数中保存数据?
【发布时间】:2013-10-17 18:22:35
【问题描述】:

在 MongoDB 中,我正在尝试编写 Map-Reduce 函数,该函数仅在满足特定条件时才保存数据。

我不知道如何从我的减速器中发射()。它总是以一种或另一种方式保存数据。

这是一个通用示例。忽略数据的上下文——我创建这个数据和代码只是为了这个问题。

数据集:

{ "_id" : ObjectId("52583b3a58da9769dda48853"), "date" : "01-01-2013", "count" : 1 }
{ "_id" : ObjectId("52583b3d58da9769dda48854"), "date" : "01-01-2013", "count" : 1 }
{ "_id" : ObjectId("52583b4258da9769dda48855"), "date" : "01-02-2013", "count" : 1 }
{ "_id" : ObjectId("52583b4f58da9769dda48856"), "date" : "01-03-2013", "count" : 4 }

地图功能:

// Map all data by (date, count)
var map = function() {
    var key = this.date;
    var value = this.count;
    emit(key, value);
}

简单地忽略不需要的数据的reducer。

// Only save dates which have count > 2
var reducer = function(date, counts) {
    var sum = Array.sum(counts);
    if (sum > 2) {
        return sum;
    }
}

结果(值 1 未被忽略):

{ "_id" : "01-01-2013", "value" : null }
{ "_id" : "01-02-2013", "value" : 1 }
{ "_id" : "01-03-2013", "value" : 4 }

我还添加了一个空的返回语句,但得到了相同的结果:

// Only save dates which have count > 2
var reducer = function(date, counts) {
    var sum = Array.sum(counts);
    if (sum > 2) {
        return sum;
    }
    else return;
}

我希望在运行 Map-Reduce 后,我的输出集合中只存在以下数据。 我怎样才能做到这一点?

{ "_id" : "01-03-2013", "value" : 4 }

【问题讨论】:

    标签: mongodb mapreduce


    【解决方案1】:

    您可以运行额外的 mapReduce 操作,具有以下功能:

    var second_map = function() { 
        if(this.value > 2) {
            emit(this._id, this.value);
        }
    }
    

    var second_reduce = function() {}
    

    reduce 函数可以为空,因为在这种情况下not having multiple values per key will cause it to not even be called

    所以,像这样运行 mapReduce:

    db.map_reduce_example.mapReduce(
        second_map, second_reduce, {out: 'final_mapreduce_result'});
    

    将产生以下集合:

    > db.final_mapreduce_result.find()
    { "_id" : "01-03-2013", "value" : 4 }
    

    请注意,如果您决定使用这种方法,您可以从第一个 reduce 函数中删除 if (sum > 2) 条件。

    【讨论】:

    • 这很聪明!我必须试一试,看看它在我的真实数据上运行时有多快,但这是实现目标的一个很好的技巧。谢谢。
    • 如果您只是想过滤一些结果,我不会做 map/reduce。只需在第一个 map/reduce 之后进行删除:db.mroutput.remove( { value : { $lte : 2 } } )。 remove() 将比 map/reduce 运行得更快。
    • @RobMoore 你是对的;这是一个更简单且可能更有效的解决方案。我认为您应该将其作为单独的答案发布。
    • 我同意@w0lf。 Rob,您应该创建一个答案。我将给出一个简单的评估来测试不同的速度并相应地接受。
    【解决方案2】:

    我们需要记住,如果键只有 1 个发射值(来自 map()),则可以跳过 reducer。我们也不应该尝试过滤reduce中的结果,因为reduce可以为同一个键多次调用(每次都使用发出值的子集)。

    唯一的其他选项是 finalize 方法,但这将导致空值而不是从结果中删除条目。

    我认为获得所需结果的唯一方法是使用聚合框架而不是 map reduce。管道看起来像:

    db.test.aggregate( 
       { 
         "$project" : { 
           "_id"   : 0, 
           "date"  : 1, 
           "count" : 1 
         } 
       }, 
       { 
         "$group" : { 
           "_id"   : "$date", 
           "value" : { "$sum" : "$count" } 
         } 
       }, 
       { 
         "$match" : { 
           "value" : { "$gt" : 2 } 
         } 
       } 
    );
    { "result" : [ { "_id" : "01-03-2013", "value" : 4 } ], "ok" : 1 }
    

    这种方法的唯一主要缺点是结果必须内联返回,这将结果的大小限制为 16MB。这将在 2.6 版本中修复/修复:https://jira.mongodb.org/browse/SERVER-10097

    HTH, 抢。

    【讨论】:

    • 我不喜欢聚合框架的主要一点是输出格式。它获取所有结果并将其存储到单个对象中。不过,我绝对感谢这个建议!并且知道 16MB 的限制将很快得到修复也是一个好消息。谢谢!
    • 2.5.2(开发版)已经实现了这个 - 聚合框架可以返回一个游标,或者你可以指定输出到一个集合。考虑到聚合框架比 map-reduce 快很多,我鼓励您尽可能使用它。
    • 此外,如果您必须为 MapReduce 提供此功能,请投票给要求此功能的jira.mongodb.org/browse/SERVER-2340。 agg 框架做不到 map reduce 可以做的事情,所以这仍然是一个有用的增强。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-03-25
    • 2013-05-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多