MongoDB中的条件upsert

时间:2014-02-08 17:14:24

标签: mongodb design-patterns

我在LinkedIn上回答了这个问题,我认为分享是有用和有趣的。问题是:

“假设我们有{_id: ..., data: ..., timestamp: ...}等文档。

有没有办法编写符合以下规则的更新标准:

1如果没有包含_id的文档,请插入此文档;

2如果存在以下_id的文档,则

2.1如果新的时间戳大于存储的时间戳,则更新data;

2.2否则什么都不做“

2 个答案:

答案 0 :(得分:9)

下面的解决方案应该可以解决问题,您只需要忽略重复键错误。示例在Mongo shell中给出:

> var lastUpdateTime = ISODate("2013-09-10")
> var newUpdateTime = ISODate("2013-09-12")
>
> lastUpdateTime
ISODate("2013-09-10T00:00:00Z")
> newUpdateTime
ISODate("2013-09-12T00:00:00Z")
>
> var id = new ObjectId()
> id
ObjectId("52310502f3bf4823f81e7fc9")
>
> // collection is empty, first update will do insert:
> db.testcol.update(
... {"_id" : id, "ts" : { $lt : lastUpdateTime } },
... { $set: { ts: lastUpdateTime, data: 123 } },
... { upsert: true, multi: false }
... );
>
> db.testcol.find()
{ "_id" : ObjectId("52310502f3bf4823f81e7fc9"), "data" : 123, "ts" : ISODate("2013-09-10T00:00:00Z") }
>
> // try one more time to check that nothing happens (due to error):
> db.testcol.update(
... {"_id" : id, "ts" : { $lt : lastUpdateTime } },
... { $set: { ts: lastUpdateTime, data: 123 } },
... { upsert: true, multi: false }
... );
E11000 duplicate key error index: test.testcol.$_id_ dup key: { : ObjectId('52310502f3bf4823f81e7fc9') }
>
> var tooOldToUpdate = ISODate("2013-09-09")
>
> // update does not happen because query condition does not match
> // and mongo tries to insert with the same id (and fails with dup again):
> db.testcol.update(
... {"_id" : id, "ts" : { $lt : tooOldToUpdate } },
... { $set: { ts: tooOldToUpdate, data: 999 } },
... { upsert: true, multi: false }
... );
E11000 duplicate key error index: test.testcol.$_id_ dup key: { : ObjectId('52310502f3bf4823f81e7fc9') }
>
> // now query cond actually matches, so update rather than insert happens which works
> // as expected:
> db.testcol.update(
... {"_id" : id, "ts" : { $lt : newUpdateTime } },
... { $set: { ts: newUpdateTime, data: 999 } },
... { upsert: true, multi: false }
... );
>
> // check that everything worked:
> db.testcol.find()
{ "_id" : ObjectId("52310502f3bf4823f81e7fc9"), "data" : 999, "ts" : ISODate("2013-09-12T00:00:00Z") }
>

唯一令人烦恼的部分是那些错误,但它们既便宜又安全。

答案 1 :(得分:-1)

    db.collection.update({
      _id: ObjectId("<id>"))
      }, 
      {timestamp: <newTimestamp>, data: <data>}, 
      {upsert: true})

如果现有文档满足条件并且现有时间戳小于newTimestamp,则此操作将更新现有文档;否则将插入新文件。