[英]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:我想通过以下方式更新此集合:
friendList
and接受 Patch API 的请求正文包含friendList
的子集和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 在进行比较以检查数组中是否存在值时会考虑name
和flag
。 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.