MongoDB,有条件的插入或更新。

13

在使用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与其他服务器通信,因此mapreduce不是选择。最终结果是一组庞大且简单的值,我想以最有效的方式存储并存储特定组合的预先计算的平均值、最大值和最小值。
其中一种解决方案是为每个更新创建JS中的唯一函数对象,但我认为这不是一种有效的方法?
主要目标是减少执行此类聚合所需的时间,带宽使用次要。
2个回答

15

在MongoDB 2.6版本中,现在可以更轻松地完成这个操作,其中包括插入和更新改进。具体来说,有新增的$min$max操作符,它们执行条件更新取决于指定值和字段当前值的相对大小。

例如,以下更新:

db.scores.update( { _id: 1 }, { $max: { highScore: 950 } } )

如果950大于当前highScore的值,则有条件地更新指定文档。


3

有一种解决方案是在JS中为每个更新创建唯一的函数对象,但我认为这不是一种有效的方法?

可能无法按您希望的方式工作:

https://jira.mongodb.org/browse/SERVER-458

这种方法能高效地实现吗?

你面临的问题是希望 MongoDB 执行一个带有一些更复杂逻辑的 upsert 操作。为了有效地触发多个复杂更改,你希望利用文档级锁定。

你不是第一个想要这样做的人,不幸的是,目前尚不可用。与此类“更复杂的更新行为”相关联的存在几个服务器错误。你可能需要观察/投票以下一些内容以检查进展情况。

插入时仅使用$set, 仅在不存在时添加, 同时使用pushset, 协调与$addToSet 的“大小”.

特别是SERVER-458 最接近你想要的。


好的,那就意味着我不能太多地提高性能了 =( 感谢您的回答!SERVER-458将解决这个问题,尽管我认为$setIfBigger和$setIfSmaller作为快速修复会更容易些。 - flindeberg
你肯定可以为这些选项提交请求(jira.mongodb.org)。 - Gates VP
1
由于MongoDB现在已经具备所需的功能,因此更改了正确答案。关于SO伦理问题,我还有点不确定是否正确。 - flindeberg
1
@flindeberg,这只是15个积分而已,我有25k+和超过600个回答,所以我还是会生存下去的 :) 嘿,看起来最近3年中有4个问题实际上已经得到解决了。好样的Mongo! - Gates VP

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