【问题标题】:mapReduce not calling map nor reducemapReduce 不调用 map 也不 reduce
【发布时间】:2014-06-02 17:53:50
【问题描述】:

我刚开始使用 mongodb,但在使用 mapReduce 函数时遇到了麻烦。 由于某种原因,它似乎没有调用 map 和 reduce 函数。

这是我的代码:

@getMonthlyReports: (req, res) ->
    app_id = req.app.id
    start = moment().subtract('years', 1).startOf('month').unix()
    end = moment().endOf('day').unix()
    console.log(start)
    console.log(end)

    map = ->
        geotriggers = 0
        pushes = 0
        console.log("ok")
        date = moment(@timestamp).startOf('month').unix()
        for campaign in @campaigns
            if campaign.geotriggers?
                geotriggers += campaign.geotriggers
            else if campaign.pushes?
                pushes += campaign.pushes

        emit date,
            geotriggers: geotriggers
            pushes: pushes

    reduce = (key, values) ->
        console.log("ok")
        geotriggers = 0
        pushes = 0
        for value in values
            geotriggers += value.geotriggers
            pushes += value.pushes
        geotriggers: geotriggers
        pushes: pushes


    common.db.collection(req.app.id + "_daily_campaign_reports").mapReduce map, reduce,
        query:
            timestamp:
                $gte: start
                $lt: end

        out:
            inline: 1
    , (err, results) ->
        console.log(results)


        ResponseHelper.returnMessage req, res, 200, results

我放了一些 console.logs,似乎 map 和 reduce 函数没有被调用。 我的结果也是未定义的。

我有什么遗漏吗?

【问题讨论】:

  • Mapreduce 在 MongoDB 服务器上运行,当然没有加载 moment.js。改用标准日期函数

标签: node.js mongodb map mapreduce aggregation-framework


【解决方案1】:

除了我已经评论过您的 mapReduce 失败的原因是由于调用了您服务器上不存在的库函数 (moment.js) 之外,这并不是 mapReduce 的真正好用法。

虽然 mapReduce 有其用途,但像这样的简单聚合案例更适合 aggregation framework,因为它是原生 C++ 实现,而不是在 JavaScript 解释器中运行的 mapReduce。因此,处理速度要快得多。

您只需要 startend 的现有 unix 时间戳值以及当月日期 (dayOfMonth) 即可进行日期数学运算:

db.collection.aggregate([
    // Match documents using your existing start and end values
    { "$match": {
        "timestamp": { "$gte": start, "$lt": end }
    }},

    // Unwind campaigns array
    { "$unwind": "$campaigns" },

    // Group on the start of month value
    { "$group": {
        "_id": { 
            "$subtract": [
               "$timestamp",
               { "$mod": [ "$timestamp", 1000 * 60 * 60 * 24 * dayOfMonth ] }
            ]
        },
        "geotriggers": { 
            "$sum": {
                "$cond": [
                   "$campaigns.geotriggers",
                   1,
                   0
                ]
            }
        },
        "pushes": { 
            "$sum": {
                "$cond": [
                   "$campaigns.pushes",
                   1,
                   0
                ]
            }
        },
    }}
])

如果我正确阅读了您的代码,您的每个文档都包含一个“活动”数组,因此要在聚合框架中处理此问题,您可以使用$unwind 管道阶段将每个数组成员公开为它自己的文档。

日期数学是在$group 阶段为_id 键完成的,方法是将“时间戳”值更改为等于月份的开始日期,这与您的代码尝试执行的操作相同。您是否可以在这里使用 null 值得商榷,因为您的范围选择只会产生一个单一的日期值,但这只是为了表明日期数学是可能的。

使用“unwound”数组元素,我们处理每个元素就像“for 循环”一样,并使用$cond 运算符有条件地添加“geotriggers”和“pushes”的值。这再次假设您的代码这些字段评估为布尔值 true/false,这是 $cond 的评估部分

您的查询条件当然只是在管道开始时遇到$match 阶段,使用相同的范围查询。

这基本上做同样的事情,而不依赖于服务器端处理中的其他库,而且速度也快得多。

请参阅其他 Aggregation Framework 运算符以供参考。

【讨论】:

  • 你知道我怎样才能从聚合中的时间戳中得到这个 dayOfMonth 吗?
  • @Lucas 提取的实际日期数学对于可用的数学运算符来说有点棘手,闰年计算等等。最好的办法是通过您的代码库计算该值并传入该值。
  • 感谢尼尔的帮助!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-02-16
  • 2012-06-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多