我正在尝试使用nodejs中的mongoose更新MongoDB中的嵌入式文档。该文档经过简化并显示如下(假设friendList
中的名称是唯一的):
{
"_id" : ObjectId("5eb0617f3aec924ff42249cd"),
"friendList" : [
{
"name" : "Alex",
"flag" : false,
},
{
"name" : "Bob",
"flag" : false,
},
{
"name" : "Caleb",
"flag" : true,
},
{
"name" : "Debbie",
"flag" : false,
}
]
}
我想通过以下方式更新此收藏集:
friendList
和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,
},
]
}
其中$ addToSet在进行比较以检查数组中是否存在值时会同时考虑name
和flag
。如果我能够在此比较阶段进行拦截,从而仅检查name
字段,则可能会起作用。
我一直在探索$[<identifier>]
和arrayFilter
之类的概念,但似乎无法使其发挥作用。
答案 0 :(得分:0)
简单的$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: { ...
}
]
);