【问题标题】:MongoDb MapReduce on child array子数组上的 MongoDb MapReduce
【发布时间】:2014-05-27 21:33:54
【问题描述】:

我在互联网上搜索了很长时间,但找不到解决此问题的方法。虽然有很多 Map reduce 示例,但我很困惑,因为我的文档有一个属性,它是一个对象数组。

我很确定这对于有经验的人来说应该很容易,但我现在是菜鸟。

我有一个大致像这样的文档

{
    _id:guid,
    clientId:guid,
    reference:'abc123'
    items:
    [
        { _id:guid, category:'A', length:100, active:true },
        { _id:guid, category:'B', length:150, active:true },
        { _id:guid, category:'A', length:10, active:false },
        { _id:guid, category:'A', length:111, active:true },
     ]
}

我想产生这个输出

dateFromIdGuid(day) category countOfItems countOfActive sumOfLength

我想保持这种格式的数据以减少写入操作的数量(每秒已经有超过 1000 次写入此集合并且还在增加)

这让我发疯了,所以非常感谢任何帮助。

谢谢。

【问题讨论】:

    标签: mongodb hadoop mapreduce aggregation-framework


    【解决方案1】:

    如果您正在谈论从 GUID 中提取时间戳并将其减少为离散的一天,那么 MongoDB 将不会对您有太大帮助。您将需要一个外部语言实现来支持这样的功能并实现一个外部 mapReduce 进程,例如使用 Hadoop。

    这让我想知道我们实际上是在谈论 GUID,还是您实际上是指 ObjectID,这将是您文档的 _id 字段的默认值,除非它已被特别覆盖以具有GUID 在那里。

    即使不是这样,您也可以通过在文档中添加某种“时间戳”字段并使用正确的 BSON Date 对象类型来帮助您,如下所示:

    {
        _id:guid,
        "timestamp": ISODate("2014-05-27T00:00:00Z")
        "clientId":guid,
        "reference":'abc123'
        "items":
        [
            { _id:guid, category:'A', length:100, active:true },
            { _id:guid, category:'B', length:150, active:true },
            { _id:guid, category:'A', length:10, active:false },
            { _id:guid, category:'A', length:111, active:true },
         ]
    }
    

    这允许您使用 MongoDB 聚合框架,因为它可以对这种类型的 Date 对象进行操作,以便将结果分解为离散的日期:

    db.collection.aggregate([
        { "$unwind": "$items" },
        { "$group": {
            "_id": { 
                "day": { "$dayOfYear": "$timestamp" },
                "category": "$items.category"
            },
            "countOfItems": { "$sum": 1 },
            "countOfActive": {
                "$sum": {
                    "$cond": [
                        "$items.active",
                        1,
                        0
                    ]
                }
            },
            "sumOfLength": { "$sum": "$items.length" }
        }} 
    ])
    

    这不仅以 MongoDB 可以做到的最快方式为您提供结果,而且“时间戳”值对于过滤日期范围内的查询也很有用,这是您无法从其他值轻松做到的事情。

    在 MongoDB mapReduce 可用的 JavaScript 中还有一种方法可以让您从 ObejctId 获取日期。不过,这比聚合框架运行得慢:

    db.collection.mapReduce(
        function() {
            var date = this._id.getTimestamp();
            items.forEach(function(item) {
                var day = 
                    "" + date.getFullyear() + 
                    "" + ( date.getMonth() + 1 ) +
                    "" + date.getDate();
                emit(
                    {
                        day: day,
                        category: item.category
                    },
                    {
                        countOfItems: 1,
                        countOfActive: ( item.active ) ? 1 : 0,
                        sumOfLength: item.length
                    }
                );
           });
        },
        function( key, values ) {
            var reduced = {
                countOfItems: 0,
                countOfActive: 0,
                sumOfLength: 0
            };
            values.forEach(function(value) {
                for ( var k in value ) {
                    reduced[k] += value[k];
                }
            });
            return reduced;
        },
        { 
            "out": { "inline": 1 }
        }
    )
    

    这基本上做同样的事情,映射器分解数组并提供分组键,而reducer只是总结来自映射器的值。因此,即使您必须从 GUID 中提取,在使用 Hadoop 时为您提供使用 Java 等语言的映射器和化简器的基本布局。

    查看aggregatemapReduce 手册页,了解有关您可以应用的选项的更多信息。

    【讨论】:

    • 我对你感激不尽。这正是我所需要的。非常感谢您的回答:)
    猜你喜欢
    • 2014-04-08
    • 2014-08-01
    • 1970-01-01
    • 2021-04-07
    • 2016-03-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-05-27
    相关资源
    最近更新 更多