如何在MongoDB集合中对所有文档的一个键的值求和

55

我在MongoDB中有一个集合:

{ "_id" : ObjectId("4d2407265ff08824e3000001"), "subida" : 3.95 }
{ "_id" : ObjectId("4d2551b4ae9fa739640df821"), "subida" : 6.03 }
{ "_id" : ObjectId("4d255b115ff08821c2000001"), "subida" : 5.53 }
{ "_id" : ObjectId("4d25e8d55ff08814f8000001"), "subida" : 1.96 }

如何对一个键(例如"subida")的值进行求和,跨所有文档?对于上述文档,我应该得到类似以下的东西:

{ "subida" : 17.47 }
5个回答

145
在这种情况下,聚合比MapReduce更简单、更高效。
db.collection.aggregate({
    $group: {
        _id: '',
        subida: { $sum: '$subida' }
    }
 }, {
    $project: {
        _id: 0,
        subida: '$subida'
    }
})
  1. 使用 $group 和 $sum 计算总和
  2. 使用投影的 $project 运算符来删除 $group 运算符所需的 id 键

2
这种方法比当前的Map/Reduce实现要高效得多。 - rdrkt
+1 优秀的答案。我在使用 {"$sum": {"subida":1 }} 方面出了问题。 - Sagar Hatekar
1
很遗憾,Spring DB目前还不支持聚合框架 :( - will824
@rdrkt - 你有什么测试/证据表明这个解决方案更快吗? - Kevin Meredith
4
MapReduce引擎是单线程的JavaScript,不使用任何索引。聚合框架是多线程的,使用本地C++编写,并在可能的情况下使用索引。 - rdrkt

18

我个人会对集合执行一次mapreduce:

map是一个简单的函数,发出“subida”字段。如果你需要单个总和,键应该是相同的;在reduce之后的结果将产生单个对象{<key>: <sum>},其中<key>是您在emit中提供的任何值。

map = function() { emit(<key>, this.subida); }

reduce也是一个简单的函数,可以将它们相加:

red = function(k, v) {
  var i, sum = 0;
  for (i in v) {
    sum += v[i];
  }
  return sum;
}
你可以在你的集合<mycollection>上调用mapreduce函数:
res = db.<mycollection>.mapReduce(map, red);

这将创建一个临时的新集合,您可以像操作其他集合一样操纵它。mapReduce返回的值包含有关mapReduce的多个值,例如花费的时间、状态...以及在"result"字段中创建的临时集合名称。 要获取需要的值,您必须查询该集合:

db[res.result].find()

这应该会给你一个对象 {<key>: <sum>}

如果你运行的是MongoDB 1.7.4或更高版本,你可以通过请求MongoDB直接返回结果而无需创建集合来节省一些麻烦:

db.<mycollection>.mapReduce(map, red, {out : {inline: 1}});

非常感谢你,朋友。我终于可以在Mongo shell中成功地执行这个查询了。我已经在我的应用程序中使用Python实现了它...现在我明白了,MongoDB的map/reduce查询与CouchDB的map/reduce查询并不相同...哈哈 :D - JAM
1
你为什么会选择MapReduce而不是聚合? - UpTheCreek

9
使用这个最简单的查询来获取结果的总和。
db.collection.aggregate({
    $group: {
        _id: '',
        subida: { $sum: '$subida' }
    }
 }
)

无法与mongoose一起使用 server error MongooseError: Mongoose 5.x不允许将操作符扩展传递给Model.aggregate()。请改用Model.aggregate([{ $match }, { $skip }]),而不是Model.aggregate({ $match }, { $skip }) - chovy

6

选项0:使用 MongoDB聚合管道 。[注意:这个选项是在问题提出后很长一段时间才添加的,但现在是正确的方法]


选项1:查询所有记录,仅返回Mongo中的subida字段,并通过在Mongo游标客户端上迭代将它们相加。

选项2:编写一个MapReduce命令,仅发出subdia字段(所有键相同),然后是总计命令。

选项3:使用db.eval在服务器上执行JavaScript:http://www.mongodb.org/display/DOCS/Server-side+Code+Execution

选项4:在插入值到集合时累加'subida'值,以便您随时拥有最新的总数。 您可以将总数存储在不同的文档中,并使用原子“如果当前更新”操作来更新它:http://www.mongodb.org/display/DOCS/Atomic+Operations


3
有没有任何例子可以更加生动易懂? - Eddy

0

您可以将集合视为数组使用,只需在循环中添加值即可。

编辑:是的,在处理大型数据集时,这是一种不好的做法。


9
大数据集上的不良实践。 - sinoohe

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接