MongoDB,条件upsert或更新

时间:2011-04-04 14:39:32

标签: javascript mongodb database

当使用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中为每个更新创建独特的函数对象,我认为这不是一种有效的方法吗?

主要目标是减少执行此类聚合所需的时间,带宽使用是次要的。

2 个答案:

答案 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 insertingadd only when not existingpush and set at the same timecoordinate a "size" with $addToSet

特别是,SERVER-458似乎与您想要的最接近。