【问题标题】:Why mongo aggregate sum takes so long?为什么mongo总和需要这么长时间?
【发布时间】:2014-04-19 09:45:21
【问题描述】:

我有一个大约 500 万个文档的表格集合。它们如下所示:

 fr to weight

对于frto,我有6000 的可能值。我创建了以下代码来将所有可能的fr 字段的所有权重加到to,例如

1 1 sum(all documents with fr=1 to=1)
1 2 sum(all documents with fr=1 to=2)
...

我使用pyMongo 编写脚本,而cells 是所有可能的fr/to 字段的数组,calls 是我拥有大约500 万份文档的集合。

for _from in cells: #pair up each cell with each other cell
     for _to in cells:
             s = calls.aggregate([
                      {
                              '$match':
                              {
                                      "fr": _from,
                                      "to": _to
                              }
                      },
                      {
                              '$group': {'_id': 'null', 'sum': {'$sum':'$weight'}}
                      }])
              if s['result']:
                     fr_to_sum = s['result'][0]['sum']
                     _id = entropy.insert({
                                     'to': _to,
                                     'fr': _from,
                                     'sum': fr_to_sum
                             })

我用的是单机。

另一个数据库会更好吗?例如。一些 SQL?

更新

字段frto 包含1-100000 范围内的整数,而weight 是一个小的float。我尝试在frto 之间进行所有可能的组合并对这些组合的值求和,因为数据库中可能存在具有frto 相同的重复文档(例如fr=1 to=2 weight=0.004 和@ 987654344@,所以在这种情况下,我想在我的entropy 表中有fr=1 to=2 weight=0.014

只是作为一个示例

 >> db.week_calls.count({'fr':10, 'to':102})
 >> 4

拨打db.week_calls.getIndexes()后我得到了

    {
    "v" : 1,
    "key" : {
        "_id" : 1
    },
    "ns" : "db.week_calls",
    "name" : "_id_"
}

这似乎很奇怪,因为我使用 db.calls.ensure_index([("fr",1), ("to",1)]) 创建并索引整个事物

【问题讨论】:

  • 你有frto 的索引吗?您选择的“范围”中有多少文档匹配。
  • 是的,我用过db.calls.ensure_index([("fr",1), ("to",1)])range 是什么意思?
  • 通过添加您的frto 标准,有多少文档得到“匹配”。通过描述更多您正在尝试做的事情,您的问题也可能会得到更好的解释。您是否想要对 frto 的所有唯一值进行总计?

标签: sql mongodb mongodb-query aggregation-framework pymongo


【解决方案1】:

你正在尝试做的事情的基本形式,没有迭代是这样的:

db.collection.aggregate([
    { "$group": {
        "_id": { 
           "fr" : "$fr",
           "to" : "$to",
        },
        "sum": { "$sum": "$weight" }
    }}
])

现在可能对你很不利,所以你可能需要做一些循环和 "range" 值如下:

db.collection.aggregate([
    { "$match": {
       "fr": { "$gt": 0, "$lte": 10000 },
       "to": { "$gt": 0, "$lte": 10000 }
    }},
    { "$group": {
           "_id": { 
                "fr" : "$fr",
                "to" : "$to",
           },
           "sum": { "$sum": "$weight" }
   }}
])

segment所有这些文档,然后得到下一个segment

db.collection.aggregate([
    { "$match": {
       "fr": { "$gt": 10000, "$lte": 20000 },
       "to": { "$gt": 10000, "$lte": 20000 }
    }},
    { "$group": {
             "_id": { 
                  "fr" : "$fr",
                  "to" : "$to"
             },
            "sum": { "$sum": "$weight" }
    }]
])

哪种解决方案最适合您。

一般而言,让$group 为您完成工作并将其分解为可管理的块,其中结果可能太大而无法一次获取。

在未来的版本中,聚合将返回一个游标。

【讨论】:

  • 谢谢!这很酷 :)。虽然现在我已经手动添加了索引,但它比以前快得多。但我也会添加您的解决方案。
  • @Pio 如果无法一次性完成。然后至少拆分范围不仅会比您当前的代码更快,而且编写起来也更干净。
  • @Pio 当前聚合将返回所有匹配(分组)项目的数组,这正是您现在处理它的方式。唯一的区别是响应中有超过 1 个元素。所以你将循环结果。在 shell 中试试看输出格式。
  • 我刚刚运行了代码,问题是它超出了内存限制。所以你的分手建议是完全有效的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-08-31
  • 1970-01-01
  • 2011-08-27
  • 2011-12-07
  • 2021-11-27
相关资源
最近更新 更多