当使用MongoDB时,我正在进行条件upsert作为聚合过程的一部分,在表单上(简化了很多):
db.dbname.update({attr1 : value1, attr2 : value2},
{"$inc" : { avg : current_value, nr : 1}},
false (multi), true (upsert))
但我希望能够保持最大(和最小)值,而无需检索文档。有点像:
db.dbname.update({ attr1 : value1, attr2 : value2},
{"$inc" : { avg : current_value, nr : 1},
"$setIfBigger" : { max : current_value}},
false (multi), true (upsert))
这有可能以有效的方式实现吗?
我当前的,效率极低的解决方案是我检查当前的聚合文档,如果它存在,我会相应地更新值,如果不存在,我会创建一个新文档。示例(再次,简化了很多,但实质是那里):
var obj = db.dbname.findOne({attr1 : value1, attr2 : value2},{_id:1});
if (obj != null) {
db.dbname.update({attr1 : value1, attr2 : value2},
{"$inc" : { avg : current_value, nr : 1},
"$set" : { max : (obj.max > current_value ? obj.max : current_value}},
false (multi), true (upsert));
} else {
db.dbname.save({attr1 : value1, attr2 : value2,
avg : current_value, nr : 1,
max : current_value});
}
实际的程序是用Java编写的,并使用mongo-API,聚合过程非常复杂,使用Javascript以外的组合技术与其他服务器通信,ergo mapreduce不是一个选项。最后,最终结果是一组非常庞大的简单值,我希望以最有效的方式存储,并存储预先计算的平均值,某些组合的最大值和最小值。
一个解决方案是在JS中为每个更新创建独特的函数对象,我认为这不是一种有效的方法吗?
主要目标是减少执行此类聚合所需的时间,带宽使用是次要的。
答案 0 :(得分:15)
现在可以在包含Insert and Update Improvements的MongoDB 2.6版本中更轻松地完成此操作。具体来说,有新的$min和$max运算符执行条件更新,具体取决于指定值的相对大小和字段的当前值。
所以例如这个更新:
db.scores.update( { _id: 1 }, { $max: { highScore: 950 } } )
如果950大于highScore
的当前值,将有条件地更新指定的文档。
答案 1 :(得分:3)
一个解决方案是在JS中为每个更新创建独特的函数对象,我认为这不是一种有效的方法吗?
可能无法按照您的意愿运作:
https://jira.mongodb.org/browse/SERVER-458
这有可能以有效的方式实现吗?
您面临的问题是您希望MongoDB使用更复杂的逻辑执行upsert。因此,您希望利用文档级锁定来一次有效地“触发”多个复杂的更改。
你不是第一个想要这个的人,不幸的是它现在还没有。有几种服务器错误与这种“更复杂的更新行为”相关联。您可能希望观看/投票以下几项以检查进度。
$set
only when inserting,add only when not existing,push
and set
at the same time,coordinate a "size" with $addToSet
。
特别是,SERVER-458似乎与您想要的最接近。