使用MongoDB更新阵列的更简便方法 [英] Easier way to Update an Array with MongoDB

查看:62
本文介绍了使用MongoDB更新阵列的更简便方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的猫鼬收藏看起来像:

My mongoose collection looks something like:

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

当一个玩家想要关注另一个用户时,我将该用户的ID添加到玩家[]数组中.

When a player wants to follow another user, I add that user's id into the players[] array.

为了实现这一点,我首先查看玩家的记录:

In order to achieve this I first look up the player's record:

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

以上内容可确保如果播放器不存在,则会创建一个播放器.

The above ensures that if the player doesn't exist, one is created.

然后我要检查'players []'数组:

Then I go about checking the 'players[]' array:

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');
  }
});

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

I was just wondering if there is a more straight-forward and lucid way of writing this query ?

推荐答案

如果您在意"在此处添加更多功能(非常建议),并在不需要返回修改后的文档的情况下限制更新的开销,或者即使这样做,也总是更好.将原子运算符与诸如 $push 的数组一起使用a>和 $addToSet .

If you "do care" about adding a bit more functionality here ( very much advised ) and limiting the overhead of updates where you really do no need to return the modified document, or even if you do then is is always better to use atomic operators with arrays like $push and $addToSet.

附加功能"还在于,在存储中使用数组时,存储项目的长度"或计数"是一种非常明智的做法.与其他获取数组计数"或使用计数/长度"进行过滤的方法相反,这在查询中变得非常有用,并且可以使用索引"进行有效访问.

The "additional functionality" is also in that when using arrays in storage, it is a really wise practice to store the "length" or "count" of items. This becomes useful in queries and can be accessed efficiently with an "index", as opposed to other methods of getting the "count" of an array or using that "count/length" for filtering purposes.

这里更好的结构是使用批量"操作作为测试对于当前的数组元素,"upserts"的概念不能很好地与之混合,因此,在需要对数组元素进行向上验证的功能时,最好在两个操作中进行数组测试.但是,由于可以通过一个请求"将批量"操作发送到服务器,并且您还会收到一个响应",因此这样做可以减轻任何实际的开销.

The better construct here is to use "Bulk" operations as testing for array elements present does not mix well with the concept of "upserts", so where you want upsert functionality an array testing it's better in two operations. But since "Bulk" operations can be sent to the server with "one request" and you also get "one response", then this mitigates any real overhead in doing so.

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
});

此方法的工作方式是,第一次尝试找到一个文档,该文档中要添加的数组元素不存在于数组中.此处不尝试更新",因为如果与数组不匹配的唯一原因是不存在数组元素,则您不想创建新文档.但是如果匹配,则将新成员添加到数组,并通过

The way this works is that the first attempt there tries to find a document where the array element to be added is not present already within the array. No attempt is made at an "upsert" here as you do not want to create a new document if the only reason it did not match a document is becasuse the array element is not present. But where matched, then the new member is added to the array and the current "count" is "incremented" by 1 via $inc, which keeps the total count or length.

因此,第二条语句仅与文档匹配,因此使用"upsert",因为如果找不到用于关键字段的文档,则会创建该文档.由于所有操作都在 $setOnInsert ,那么如果文档已经存在,将不会执行任何操作.

The second statement is therefore going to match the document only, and therefore does use an "upsert" since if the document is not found for the key field then it will be created. As all operations are inside $setOnInsert then there will be no operation performed if the document already exists.

这实际上只是一个服务器的请求和响应,因此不存在包含两个更新操作的来回"操作,从而使此操作高效.

It's all just really one server request and response, so there is no "back and forth" for the inclusion of two update operations, and that makes this efficient.

删除数组条目基本上是相反的,只是这次没有必要创建"新文档(如果找不到):

Removing an array entry is basically the reverse, except that this time there is no need to "create" a new document if it was not found:

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
});

因此,现在您只需要测试数组元素存在的位置以及数组元素的存在位置

So now you only need to test where the array element is present and where it is then $pull the matched element from the array content, at the same time as "decrementing" the "count" by 1 to reflect the removal.

现在,您可以在此处使用" $addToSet,因为它将仅查看数组内容,如果未找到该成员,则将其添加,并且出于几乎相同的原因,无需测试使用$pull时存在的数组元素,因为如果元素不存在则将不执行任何操作.在这种情况下,只要您不跨路径",就可以直接在"upsert"中使用$addToSet,因为不允许与MongoDB在同一路径上尝试使用多个更新运算符:

Now you "could" use $addToSet instead here as it will just look at the array content and if the member is not found then it will be added, and by much the same reasons there is no need to test for the array element existing when using $pull as it will just do nothing if the element is not there. Futhermore $addToSet in that context can be used directly within an "upsert", as long as you do not "cross paths" since it is not allowed to try and use multiple update operators on the same path with MongoDB:

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

但这将是错误的":

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
    }
);

但是,这样做会使计数"功能失去作用,因为这样的操作只是在不考虑实际内容或是否添加"或删除"了任何内容的情况下完成.

However, by doing that you loose the "count" functionality since such operations are just completing with no regard to what is actually there or if anything was "added" or "removed".

保留计数器"确实是一件好事,即使您现在还没有立即使用它们,那么在应用程序生命周期的某个阶段,您可能会想要它们.因此,了解所涉及的逻辑并立即实施它们非常有意义.现在要付出很小的代价,以后才能获得很多好处.

Keeping "counters" is a really good thing, and even if you do not have an immediate use for them right now, then at some stage in the lifecyle of your application you are probably going to want them. So it makes a lot of sense to understand the logic involved and implement them now. Small price to pay now for a lot of benefit later.

此处提供简短的旁注,因为我通常建议尽可能进行批量"操作.通过.collection访问器在猫鼬中使用此方法时,您需要了解这些是本机驱动程序方法,因此其行为与猫鼬"方法不同.

Quick sidenote here as I generally recommend "Bulk" operations where possible. When using this via the .collection accessor in mongoose, then you need to be aware that these are native driver methods and therefore behave differently than the "mongoose" methods.

值得注意的是,所有猫鼬"方法都有一个内置的检查"选项,以查看与数据库的连接当前处于活动状态.否则,将有效地排队"该操作,直到建立连接为止.使用本机方法,此检查"不再存在.因此,您需要确定执行"first"操作的"mongoose"方法已经存在连接,或者将整个应用程序逻辑包装在等待"建立连接的结构中:

Notably, all "mongoose" methods have a buit-in "check" to see that the connection to the database is currently active. Where it is not, the operation is effectively "queued" until the connection is made. Using the native methods this "check" is no longer present. Therefore you either need to be sure that a connection is already present from a "mongoose" method having executed "first", or alternately wrap you whole application logic in a construct that "waits" for the connection to be made:

mongoose.connection.on("open",function(err) {
    // All app logic or start in here
});

这样,您可以确保存在连接,并且方法可以返回并使用正确的对象.但是没有连接,批量"操作将失败.

That way you are sure that there is a connection present and the correct objects can be returned and used by the methods. But no connection, and the "Bulk" operations will fail.

这篇关于使用MongoDB更新阵列的更简便方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆