使用MongoDB更新阵列的更简单方法

时间:2015-09-07 16:30:53

标签: mongodb mongoose mongodb-query

我的mongoose系列看起来像:

var followSchema = new Schema({
  facebookId: {type: String, required: true},
  players : [],
  fans: [],
});

当玩家想要关注其他用户时,我会将该用户的ID添加到player []数组中。

为了达到这个目的,我首先查看玩家的记录:

var myRecord = FollowModel.findOneAndUpdate(
      {facebookId: req.user.facebookId},
      {$setOnInsert: {}},
      {upsert: true, new : true}
    );

以上确保如果玩家不存在,则创建一个。

然后我去检查“玩家[]'阵列:

myRecord.exec(function(err, result) {
  if (err) {
    throw err;
  }

  if (result.players.indexOf(req.body.idToFollow) < 0) {
    result.players.push(req.body.idToFollow);
    result.save();
    res.status(200).send('Player added to Follow List');
  } else {
    res.status(200).send('Already Following this player');
  }
});

我只是想知道是否有更直接和清晰的方式来编写此查询?

2 个答案:

答案 0 :(得分:0)

如果您不在乎知道已经创建了以下内容,那么您可以在初始查询中使用$ addToSet表示法:

var myRecord = FollowModel.findOneAndUpdate(
      {facebookId: req.user.facebookId},
      {$setOnInsert: {},
       $addToSet: {players: req.body.idToFollow}},
      {upsert: true, new : true}
    );

我不确定你用那个空的$ setOnInsert做了什么。

答案 1 :(得分:0)

如果你&#34;照顾&#34;关于在这里添加更多功能(非常建议)并限制更新的开销,你真的不需要返回修改后的文档,或者即使你这样做也总是更好地使用像{{3这样的数组的原子操作符}和$push

&#34;附加功能&#34;也就是说,在存储中使用数组时,存储&#34;长度&#34;是一种非常明智的做法。或&#34;计数&#34;的项目。这在查询中很有用,并且可以使用&#34; index&#34;进行有效访问,而不是其他获取&#34; count&#34;的方法。数组或使用&#34;计数/长度&#34;用于过滤目的。

这里更好的构造是使用$addToSet作为对存在的数组元素的测试与&#34; upserts&#34;的概念不能很好地混合,所以你想要upsert功能的一个数组测试它&#39在两次行动中表现更好。但自从&#34; Bulk&#34;操作可以通过&#34;一个请求发送到服务器&#34;并且你也得到了一个响应&#34;然后这减轻了这样做的任何实际开销。

var bulk = FollowModel.collection.initializeOrderedBulkOp();

// Try to add where not found in array
bulk.find({ 
    "facebookId": req.user.facebookId,
    "players": { "$ne": req.body.idToFollow }
}).updateOne({
    "$push": { "players": req.body.idToFollow },
    "$inc": { "playerCount": 1 }
});

// Otherwise create the document if not matched
bulk.find({
    "facebookId": req.user.facebookId,
}).upsert().updateOne({
    "$setOnInsert": {
        "players": [req.body.idToFollow]
        "playerCount": 1,
        "fans": [],
        "fanCount": 0
    }
})

bulk.execute(function(err,result) {
    // Handling in here
});

这种方法的工作原理是第一次尝试找到一个文档,其中要添加的数组元素已经不存在于数组中。没有尝试过&#34; upsert&#34;这里因为您不想创建新文档,如果它与文档不匹配的唯一原因是因为数组元素不存在。但是匹配的地方,然后将新成员添加到数组中并且当前&#34;计数&#34;是&#34;递增&#34;通过"Bulk" operations 1来保持总计数或长度。

因此,第二个语句仅与文档匹配,因此确实使用&#34; upsert&#34;因为如果没有找到关键字段的文档,那么它将被创建。由于所有操作都在$inc内,因此如果文档已经存在,则不会执行任何操作。

它只是一个服务器请求和响应,所以没有&#34;来回&#34;包含两个更新操作,这使得效率更高。

删除数组条目基本上是相反的,除了这次不需要&#34;创建&#34;如果找不到新文件:

var bulk = FollowModel.collection.initializeOrderedBulkOp();

// Try to remove where found in array
bulk.find({ 
    "facebookId": req.user.facebookId,
    "players": req.body.idToFollow
}).updateOne({
     "$pull": { "players": req.body.idToFollow },
     "$inc": { "playerCount": -1 }
});

bulk.execute(function(err,result) {
    // Handling in here
});

所以现在你只需要测试数组元素的存在位置以及数组内容的匹配元素$setOnInsert,同时测量&#34;递减&#34; &#34;计数&#34;用1表示反映删除。

现在你可以&#34;在这里使用$addToSet,因为它只会查看数组内容,如果找不到成员,那么它将被添加,并且由于相同的原因,在使用时不需要测试存在的数组元素{ {1}}因为如果元素不在那里它就什么都不做。只要你没有&#34;交叉路径&#34;那么在该上下文中的其他$pull可以直接在&#34; upsert&#34;中使用。因为不允许在与MongoDB相同的路径上尝试使用多个更新运算符:

$addToSet

但这将是&#34;错误&#34;:

FollowModel.update(
    { "facebookId": req.user.facebookId },
    {
        "$setOnInsert": {
            "fans": []
        },
        "$addToSet": { "players": req.body.idToFollow }
    },
    { "upsert": true },
    function(err,numAffected) {
        // handling in here
    }
);

但是,通过这样做,你可以放弃&#34;计数&#34;功能,因为这样的操作只是完成而不考虑实际存在的内容或者是否添加了什么&#34;或&#34;删除&#34;。

保持&#34;计数器&#34;是一件非常好的事情,即使你现在没有立即使用它们,那么在应用程序的生命周期的某个阶段你可能会想要它们。因此理解所涉及的逻辑并立即实现它们是很有意义的。现在要付出很小的代价以获得很多好处。

这里的快速旁注,我通常建议&#34;批量&#34;尽可能的操作。当通过mongoose中的FollowModel.update( { "facebookId": req.user.facebookId }, { "$setOnInsert": { "players": [], // <-- This is a conflict "fans": [] }, "$addToSet": { "players": req.body.idToFollow } }, { "upsert": true }, function(err,numAffected) { // handling in here } ); 访问器使用它时,您需要知道这些是本机驱动程序方法,因此行为与&#34; mongoose&#34;方法

值得注意的是,所有&#34; mongoose&#34;方法有一个buit-in&#34; check&#34;看到与数据库的连接当前是否处于活动状态。如果不是,那么该操作就会有效地排队等待。直到建立连接。使用本机方法,检查&#34;不再存在因此,您需要确保已经存在来自&#34; mongoose&#34;已执行&#34; first&#34;的方法,或者将整个应用程序逻辑包装在一个&#34;等待&#34;要进行连接:

.collection

这样您就可以确定存在连接,并且方法可以返回并使用正确的对象。但没有联系,而且&#34; Bulk&#34;操作将失败。