简体   繁体   English

如何根据 MongoDB 中的另一个数组更新嵌入式文档中的字段

[英]How do I update a field in embedded documents based on another array in MongoDB

I am trying to update an embedded document in MongoDB using mongoose in nodejs.我正在尝试使用 nodejs 中的 mongoose 更新 MongoDB 中的嵌入式文档。 The document is simplified and shown below (The names in friendList is assumed to be unique):文档被简化如下所示( friendList中的名称假定是唯一的):

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

I would like to update this collection by:我想通过以下方式更新此集合:

  1. accepting a Patch API with a request body containing a subset of friendList and接受 Patch API 的请求正文包含friendList的子集和
  2. update the nested field flag .更新嵌套字段flag

For example, if I were to do a patch call from postman with the request body:例如,如果我要使用请求正文从 postman 进行补丁调用:

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

then I should expect my document in MongoDB to look like this:那么我应该期望我在 MongoDB 中的文档看起来像这样:

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

What I have tried on nodejs is updating the entire request body:我在 nodejs 上尝试过的是更新整个请求正文:

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

which replaces the entire friendList array:它替换了整个friendList数组:

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

I have also tried using array operators like $ :我也尝试过使用像$这样的数组运算符:

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

which gave me the output:这给了我 output:

{
    "_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 considers both name and flag when making a comparison to check if the values exist in the array. $addToSet 在进行比较以检查数组中是否存在值时会考虑nameflag It might work if I am able to intercept at this comparison phase such that only the name field is checked.如果我能够在这个比较阶段进行拦截以便只检查name字段,它可能会起作用。

I have been exploring concepts like $[<identifier>] and arrayFilter but can't seem to make it work.我一直在探索诸如$[<identifier>]arrayFilter类的概念,但似乎无法使其发挥作用。

Simple $addToSet does not work, because your array is not ["Alex","Caleb","Debbie"] .简单的$addToSet不起作用,因为您的数组不是["Alex","Caleb","Debbie"] Your array is你的数组是

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

Element {name:"Alex", flag: true} is different to element {name: "Alex", flag: false} , that's the reason why your approach failed. Element {name:"Alex", flag: true}与 element {name: "Alex", flag: false}不同,这就是您的方法失败的原因。 I think you have to use aggregation pipeline, eg this one:我认为你必须使用聚合管道,例如这个:

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" }
])

Or if you like to work with index variable:或者,如果您喜欢使用索引变量:

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" }
])

Note, this will update only existing names.请注意,这将仅更新现有名称。 New names are not added to the array, your question is not clear in this regard.新名称未添加到数组中,您的问题在这方面不清楚。 If you like to add also new elements then insert如果您还想添加新元素,请插入

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

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

The aggregation pipeline can be used in an update:聚合管道可用于更新:

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 基于匹配的查询MongoDB NodeJS更新所有文档和嵌入文档中的字段 - Update a field in all documents and embedded documents based on matching query MongoDB NodeJS MongoDB-Mongoose:如何使用Mongoose同时更新两个单独的嵌入式文档? - MongoDB - Mongoose: How Do I Update Simultaneously Two Separate Embedded Documents With Mongoose? 如何查询嵌入数组中的嵌入文档 mongodb - How to query embedded documents in an embedded array mongodb MongoDB如何基于对象数组更新多个文档 - MongoDB How to update multiple documents based on array of objects 如何在 mongodb 中涉及数组或其他类型的嵌入或嵌套文档的更新操作中使用变量? - How can I use variables in an update operation in mongodb that involves an array or another type of embedded or nested document? 在MongoDB中的多个文档中更新多个嵌入式文档 - Update multiple embedded documents in multiple documents in MongoDB MongoDb:如何根据每个字段的值更新多个文档中的字段值? - MongoDb: How to update a field value in many documents based on the value of each field? 在MongoDB中如何根据另一个字段有条件地更新具有不同值的文档 - In MongoDB how to update a document with different values conditionally based on another field 在nodejs中,如何在嵌入的mongodb文档之间遍历以获取其键的值 - In nodejs, how do I traverse between embedded mongodb documents to get the values of their keys MongoDB 请求:仅从嵌入数组中的嵌入文档中过滤特定字段 - MongoDB request: Filter only specific field from embedded documents in embedded array
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM