【问题标题】:How do I update a field in embedded documents based on another array in MongoDB如何根据 MongoDB 中的另一个数组更新嵌入式文档中的字段
【发布时间】:2020-06-23 18:58:04
【问题描述】:

我正在尝试使用 nodejs 中的 mongoose 更新 MongoDB 中的嵌入式文档。文档被简化如下所示(friendList 中的名称假定是唯一的):

{
    "_id" : ObjectId("5eb0617f3aec924ff42249cd"),
    "friendList" : [ 
        {
            "name" : "Alex",
            "flag" : false,
        },
        {
            "name" : "Bob",
            "flag" : false,
        },
        {
            "name" : "Caleb",
            "flag" : true,
        },        
        {
            "name" : "Debbie",
            "flag" : false,
        }
    ]
}

我想通过以下方式更新此收藏:

  1. 接受带有包含friendList 子集的请求正文的 Patch API 和
  2. 更新嵌套字段flag

例如,如果我要使用请求正文从邮递员进行补丁调用:

{
    "friendList":[
        {
            "name":"Alex",
            "flag":true
        },
        {
            "name":"Caleb",
            "flag":false
        },
        {
            "name":"Debbie",
            "flag":false
        }
    ]
}

那么我应该期望我在 MongoDB 中的文档看起来像这样:

{
    "_id" : ObjectId("5eb0617f3aec924ff42249cd"),
    "friendList":[
        {
            "name":"Alex",
            "flag":true
        },
        {
            "name":"Bob",
            "flag":false
        },
        {
            "name":"Caleb",
            "flag":false
        },
        {
            "name":"Debbie",
            "flag":false
        }
    ]
}

我在 nodejs 上尝试过的是更新整个请求正文:

function updateUser(req){
    User.findOneAndUpdate({'_id':req.params._id},req.body,{new:true});
}

替换整个friendList数组:

{
    "_id" : ObjectId("5eb0617f3aec924ff42249cd"),
    "friendList":[
        {
            "name":"Alex",
            "flag":true
        },
        {
            "name":"Caleb",
            "flag":false
        },
        {
            "name":"Debbie",
            "flag":false
        }
    ]
}

我也尝试过使用数组运算符,例如$:

function updateUser(req){
    User.findOneAndUpdate(
        {'_id':req.params._id},
        {$addToSet:{
            "friendList":{
                $each:req.body.friendList}
            }
        },
        {new:true}
    );
}

这给了我输出:

{
    "_id" : ObjectId("5eb0617f3aec924ff42249cd"),
    "friendList" : [ 
        {
            "name" : "Alex",
            "flag" : false,
        },
        {
            "name" : "Bob",
            "flag" : false,
        },
        {
            "name" : "Caleb",
            "flag" : true,
        },        
        {
            "name" : "Debbie",
            "flag" : false,
        },
        {
            "name" : "Alex",
            "flag" : true,
        },
        {
            "name" : "Caleb",
            "flag" : false,
        },
    ]
}

which $addToSet 在进行比较以检查数组中是否存在值时同时考虑nameflag。如果我能够在此比较阶段进行拦截以便仅检查 name 字段,它可能会起作用。

我一直在探索$[<identifier>]arrayFilter 之类的概念,但似乎无法实现。

【问题讨论】:

  • 我没有测试但可能很简单{$addToSet:{"friendList": req.body.friendList } }
  • 嗨@WernfriedDomscheit,我试过你的方法,我得到的输出与{ $addToSet : { "friendList" : { $each : req.body.friendList } }相同,其中nameflag在进行比较时被视为1个实体。

标签: node.js mongodb api


【解决方案1】:

简单的$addToSet 不起作用,因为您的数组不是["Alex","Caleb","Debbie"]。你的数组是

[ 
  {name: "Alex", flag: true}, 
  {name: "Caleb", flag: false}, 
  {name: "Debbie", flag: false} 
]

元素{name:"Alex", flag: true} 与元素{name: "Alex", flag: false} 不同,这就是您的方法失败的原因。我认为您必须使用聚合管道,例如这个:

db.collection.aggregate([
   { $addFields: { newFriends: friendList } },
   {
      $set: {
         friendList: {
            $map: {
               input: "$friendList",
               in: {
                  name: "$$this.name",
                  flag: {
                     $cond: [
                        { $eq: [{ $indexOfArray: ["$newFriends.name", "$$this.name"] }, - 1] },
                        "$$this.flag",
                        { $arrayElemAt: [ "$newFriends.flag", { $indexOfArray: ["$newFriends.name", "$$this.name"] } ] }
                     ]
                  }
               }
            }
         }
      }
   },
   { $unset: "newFriends" }
])

或者如果你喜欢使用索引变量:

db.collection.aggregate([
   { $addFields: { newFriends: friendList } },
   {
      $set: {
         friendList: {
            $map: {
               input: "$friendList",
               in: {
                  $let: {
                     vars: { idx: { $indexOfArray: ["$newFriends.name", "$$this.name"] } },
                     in: {
                        name: "$$this.name",
                        flag: {
                           $cond: [
                              { $eq: ["$$idx", - 1] },
                              "$$this.flag",
                              { $arrayElemAt: ["$newFriends.flag", "$$idx"] }
                           ]
                        }
                     }
                  }
               }
            }
         }
      }
   },
   { $unset: "newFriends" }
])

注意,这只会更新现有名称。新名称未添加到数组中,您的问题在这方面不清楚。如果您还想添加新元素,请插入

{
   $set: {
      friendList: { $setUnion: ["$friendList", "$newFriends"] }
   }
},

就在{ $unset: "newFriends" }之前

聚合管道可用于更新:

User.findOneAndUpdate(
    {'_id':req.params._id},
    [
      { $addFields: { newFriends: req.body.friendList } },
      {
         $set: { ...
      }
    ]
);

【讨论】:

    猜你喜欢
    • 2018-12-08
    • 2016-12-18
    • 1970-01-01
    • 2021-10-31
    • 2020-06-29
    • 1970-01-01
    • 1970-01-01
    • 2020-02-07
    • 2016-05-04
    相关资源
    最近更新 更多