2016-11-23 56 views
2

我想实现在MongoDB中一个'server-side counter-versioned'项目,并尝试使用Java API */

Document dbDoc = dbCollection.findOneAndUpdate(
     new Document("_id", "meta"), 
     new Document("$inc", new Document("version", 1)) 
      .append("$setOnInsert", new Document("version", 0)), 
     new FindOneAndUpdateOptions().upsert(true) 
            .returnDocument(ReturnDocument.AFTER)); 

假定逻辑是做以下/ * 很简单:如果数据库中没有记录 - 从零开始计数(并且带有全新的新对象),否则 - 增加计数器。

代码示例失败:'Cannot update 'version' and 'version' at the same time'

我的假设是,如果没有匹配的项目被发现在“更新插入”模式蒙戈应该只用“$ setOnInsert” - 但它在一些其他的方式工作。

是否可以在实现这样的操作一个原子 mongoDB调用?


PS:MongoDB documentation关于findOneAndUpdate()和UPSERT()是模糊的 - 至少我可以不知道为什么,从他们的描述这个错误arizes。

也有类似的问题在这里 - findAndModify fails with error: "Cannot update 'field1' and 'field1' at the same time - 接受,但再次没有明确的推理。

+0

我想添加这个例子中,当你有值的嵌套的对象:{_id:1,小时:{0:0,1:0,2:0 }}并且如果文档不存在或只是想要添加并增加一小时如果有的话,增加一小时。其实这是不可能的,也不违反任何逻辑。 – jtomasrl

回答

1

你可以仅仅删除的$setOnInsert,因为这将更新操作将被设置为在$inc运营商指定的值,如果这个文件不存在

https://docs.mongodb.com/v3.2/reference/operator/update/inc/#behavior

如果该字段不存在, $ inc创建该字段并将该字段设置为指定的值。从蒙戈外壳

例子:

> db.dropDatabase() 
{ "ok" : 1 } 
> db.test.findOneAndUpdate({_id: "meta"}, { $inc: { version: 1} }, {upsert: true, returnNewDocument: true}) 
{ "_id" : "meta", "version" : 1 } 
> db.test.findOneAndUpdate({_id: "meta"}, { $inc: { version: 1} }, {upsert: true, returnNewDocument: true}) 
{ "_id" : "meta", "version" : 2 } 
> db.test.findOneAndUpdate({_id: "meta"}, { $inc: { version: 1} }, {upsert: true, returnNewDocument: true}) 
{ "_id" : "meta", "version" : 3 } 
> db.test.findOneAndUpdate({_id: "meta"}, { $inc: { version: 1} }, {upsert: true, returnNewDocument: true}) 
{ "_id" : "meta", "version" : 4 } 
> db.test.findOneAndUpdate({_id: "meta"}, { $inc: { version: 1} }, {upsert: true, returnNewDocument: true}) 
{ "_id" : "meta", "version" : 5 } 

如果您需要设置一个给定的版本,第一个初始插件,然后MongoDB中不支持任何运营商原子支持这一点,但后续会安全是一个常见的解决方法:

> db.dropDatabase() 
{ "dropped" : "test", "ok" : 1 } 
> function updateMeta(){ 
... function update(){ 
...  return db.test.findOneAndUpdate({_id: "meta"}, { $inc: { version: 1} }, {returnNewDocument: true}); 
... } 
... 
... var result = update(); 
... 
... if(result === null){ 
...  db.test.insert({_id: "meta", version: -10}); 
...  result = update(); 
... } 
... 
... return result; 
... } 
> 
> updateMeta() 
{ "_id" : "meta", "version" : -9 } 
> updateMeta() 
{ "_id" : "meta", "version" : -8 } 
> updateMeta() 
{ "_id" : "meta", "version" : -7 } 
> updateMeta() 
{ "_id" : "meta", "version" : -6 } 
> updateMeta() 
{ "_id" : "meta", "version" : -5 } 
> 
+0

这不完全是我所需要的 - 我想以'0'(零)(或可能是其他数字)作为初始计数器值开始。 –

+0

@XtraCoder更新了该变体的答案。 –

+0

该变化不是**一个原子mongoDB调用**,这意味着从许多不同的客户端并行执行相同的功能将导致不可预知的结果。 –