【问题标题】:How to achieve "Insert or Increment" in one MongoDB query?如何在一个 MongoDB 查询中实现“插入或增量”?
【发布时间】:2015-12-31 09:27:21
【问题描述】:

我想在 MongoDB 中编写一个查询,以在文档中插入一个对象(如果它不存在),或者如果它存在于指定的文档中,则增加它的某个字段。下面以两个查询为例:

文档结构:

     Movies:
     {
         integer _id,
         string Name,
         array(object) WatchingList
         [
            {
                integer UserID,
                integer WatchTimes
            }
            .....
         ]
     }

插入查询:

db.Movies.update({_id:x}, {$push: {WatchingList:{UserID:y,WatchTimes:1}}});

增量查询:

db.Movies.update({_id:x,"WatchingList.UserID":y},{$inc:{"WatchingList.$.WatchTimes":1}});

如何将它们组合成一个或多个查询以达到要求?

【问题讨论】:

  • 我可以看看你的文档结构吗?在我看来,你的模型已经犯了错误,我们应该在做任何其他事情之前纠正它。
  • 我已经添加了文档结构,你可以看看。
  • 先增加观看次数再更新?我的意思是获取观看时间做一个 +1 然后更新。
  • @Abhishek Dey 是的,你可以这样做。但你需要确保我写的两个查询可以组合在一起。
  • @AbhishekDey 这不是原子的并且容易受到竞争条件的影响。

标签: mongodb mongodb-query database


【解决方案1】:

根据您的问题,我了解到您需要通用更新方法来插入和更新数组。可以使用数组中的虚拟对象作为最后一个元素来完成,并在插入的情况下更新该元素。

我在这里举个例子: 最初将虚拟对象添加到数组中:

      > db.M4.update({_id:"id"},{"$addToSet":{WatchingList:{UserID:"Last",WatchTimes:0}}},true,true)
      > db.M4.find()
        { "_id" : "id", "WatchingList" : [ { "UserID" : "Last", "WatchTimes" : 0 } ] }

//现在下面的查询将同时处理插入和更新条件:

     db.M4.update({_id:"id","WatchingList.UserID":{"$in":["p","Last"]}},{$inc:{"WatchingList.$.WatchTimes":1},"$set":{"WatchingList.$.UserID":"p"}});

记得在每个更新语句之前运行第一个虚拟添加。或者您可以简单地添加大量虚拟对象最初本身

【讨论】:

  • @IT_Layman 你能解释一下我的回答有什么问题吗?我只是想知道我是否理解你的问题......
  • 我很抱歉这么晚才给你反馈。其实我只是一个 MongoDB 初学者,不太明白你的答案。如果您能提供一个关于我的问题如何运作的示例,我将不胜感激。
  • @IT_Layman 可以感谢您的回复。我的想法是在“WatchingList”数组中保留一个虚拟对象 { "UserID" : "Last", "WatchTimes" : 0 } 并运行给定的单个更新查询。如果 UserId 已经存在,则该查询将增加计数,否则它将向数组中插入一个新用户。这里只有一个用于更新和插入的查询,前提是您在数组中有虚拟对象。如果您需要更多说明,请咨询我...
【解决方案2】:

好的,首先,我认为您的模型有误。 在撰写本文时,BSON 文档的最大大小为 16MB。除非很少有人会看电影,否则这将是一个问题。

对于您的原始模型,您需要的是使用$addToSet$inc 的更新:

db.movies.update(
  { "_id": x },
  { $addToSet:
    { "Watching.List":
      { UserId: foo, "$inc":{ WatchTimes: 1 } }
    }
  }
)

免责声明 我没有对此进行测试,但它应该可以工作,因为如果 $inc 不存在,它应该创建一个字段,并且 $addToSet 应该为数组创建一个唯一条目。

【讨论】:

  • 我认为您的担心是正确的,我决定将 watchList 字段嵌入到用户文档中。所以更新后的文档结构如下:
  • 由于更改不会影响我提出的问题,因此我不需要编辑我的问题,您也不需要编辑您的答案。
  • 我刚试了一下,还是不行。 $inc 在 Mongo shell 上显示为字段名。
  • 我在公司周围添加了一些引号,请检查。编辑你认为合适的模型,我会很乐意相应地编辑我的答案。
  • 还是不行,错误信息显示$inc对存储无效。
【解决方案3】:

这对我来说似乎适用于 node.js 3.x mongodb 驱动程序:

await db.collection('collection-name').updateOne({name:"foo"}, {$inc:{counter:10}}, {upsert:true});

如果带有name=foo 的文档不存在,它会创建它并将计数器设置为 10。如果它已经存在,则将 10 添加到计数器。

如果你需要一次执行一堆命令,你应该使用bulkWrite以获得更好的性能:

let ops = [];
ops.push({ updateOne: { filter: {name:"foo"}, update: {$inc: {counter:10} }, upsert:true } });
// <add more bulkWrite objects to ops array>
await db.collection('collection-name').bulkWrite([op], {ordered:false});

【讨论】:

    猜你喜欢
    • 2019-11-05
    • 1970-01-01
    • 2019-04-15
    • 1970-01-01
    • 2011-04-19
    • 1970-01-01
    • 2014-06-26
    • 1970-01-01
    • 2021-11-22
    相关资源
    最近更新 更多