更新MongoDB中的嵌套对象(如果存在),否则添加它

时间:2016-09-29 22:46:22

标签: node.js mongodb

我在mongoDB中有这样的json doc:

 { "_id" : ObjectId("57ed88c0965bedd2b11d5727"),
   "refid" : 2,
   "votes" : [
     { "ip" : "127.0.2.1", "rating" : 5 },
     { "ip" : "127.0.3.1", "rating" : 2 },
     { "ip" : "127.59.83.210", "rating" : 50 },
     { "ip" : "127.56.26.191", "rating" : 5 },
     { "ip" : "127.59.83.210", "rating" : 5 },
     { "ip" : "127.59.83.210", "rating" : 30 }
    ]
 }

而不是为每个IP创建dupes,我想更新IP存在时找到的记录,或者如果不存在则添加新条目。有很多关于如何进行类似操作的问题,但没有一个我能找到这个简单的问题。我在Node.js中尝试这个:

db.collection('ratings').update( { "refid":refid, "votes.ip":ip },
  {
    $push: { votes: { "ip":ip, "rating":rating }
  }
})

但是当我从同一个IP投票时,它只是不断添加新条目。

注意:我没有使用Mongoose。

如果我的问题过于简单,我很抱歉,我对MongoDB很新。我只是想知道是否有一个很好的方法来完成这个,而不是以编程方式迭代“投票”中的每个记录。谢谢!

编辑:我要求的是目前无法解决的问题,我在下面标记了一个正确的答案,其中包含解释以及MongoDB上的故障单链接以进行增强。

至于我在这里提出的问题,下面的代码对我有用。我不知道它在性能方面有多好或多坏,但它涵盖了所有这些场景:

  • 如果记录根本不存在,请创建
  • 如果记录存在,请查找IP,如果找到,请更新评级
  • 如果记录存在但IP没有,请插入新的评级

使用:

db.collection('ratings').findOne({ "refid":refid }).then(function(vote) {  
  if (vote == null) {
    newrating = {refid: refid, votes: [{ ip: ip, rating: rating }]};
    db.collection('ratings').save(newrating);
  } else {
    db.collection('ratings').findOne({ "refid":refid, "votes.ip":ip }).then(function(rate) {
      if (rate != null) {
        db.collection('ratings').update(
          { refid: refid, "votes.ip" : ip },
          { $set: { "votes.$.rating" : rating } }
        )
      } else {
        db.collection('ratings').update({"refid":refid,"votes.ip" : {$ne : ip }},
          { $push: { votes: { "ip" : ip, "rating" : rating, }}}
        )
      }
    })
  }
})

5 个答案:

答案 0 :(得分:2)

如果不存在则插入文档由upsert完成,如果要更新条件嵌入文档,则需要$ position操作符。因此,您需要在查询中同时使用它们来实现上述功能。

但是现在mongodb不支持使用$ position运算符进行upserting

  

不要将位置运算符$与upsert运算一起使用,因为,   insert将在插入的文档中使用$作为字段名称。

所以你想要的是现在不能在一个查询中做到这一点,或者你可以在两个查询中做到这一点。

首先

db.collection('ratings')).update(
  {"refid":refid, "votes.ip": ip},
  {
     $set: { "votes.$.rating":rating }
  }
)

它返回更新的文档数,如果是1则没问题,如果是0,则需要推送新记录。

db.collection('ratings')).update( { "refid":refid, "votes.ip":{$ne: ip}},
    {$push: { votes: { "ip":ip , "rating":rating  }}
})

还有位置操作员和upserting的jira票,如果你想在mongodb中使用这个功能,请投票支持这个问题。以下是问题链接

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

答案 1 :(得分:0)

也许

db.collection('ratings').update( { "refid":refid, "ip":ip },
   {
      $addToSet: { votes: { "ip":ip, "rating":rating }
   }
})

答案 2 :(得分:0)

试试这个

db.collection('ratings')).update( { "refid":refid, "votes.ip":ip , "votes.rating":{$ne: rating }},
    {$push: { votes: { "ip":ip , "rating":rating  }}
})

如果给定的ip不存在评级,它将添加新记录,如果给定的ip和评级存在记录,则不添加重复记录。

答案 3 :(得分:0)

也可以通过这种方式解决。这是我用于工作的那个。

拉出匹配值并按下面所示推送新值:

db.collection(‘ratings’).update({“refid”:refid},
         {$pull: {votes: {“ip": ip}}},
    {multi: true});

db.collection("ratings").update({“refid":refid},
     {$push:{‘votes’:{"ip":ip,"rating":rating}}})

答案 4 :(得分:0)

如果refid存在,则此代码即可完成工作

let bulk = await db.collection('ratings').initializeOrderedBulkOp();

//edit if exist
await bulk.find({ refid: refid, 'votes.ip': ip })
          .updateOne({ $set: {'votes.$.rating': rating} });
//add if doesn't exist
await bulk.find({ refid: refid, 'votes.ip': {'$ne': ip} })
          .updateOne({ $push: {votes: {ip: ip, rating: rating}} });

await bulk.execute();