我遇到麻烦的MongoDB中使用Mongoose的东西。
使用像这样的相当简单的模式
const UserSchema = new Schema({
groups: [
{
name: String,
members: [
{
hasAccepted: {
type: Boolean
}
}
]
}
]
});
当我创建新组时,每个成员对象当然都会获得一个_id属性。我只想通过其_id选择该成员并更新其hasAccepted属性。
当我使用成员的_id运行查询时,我会返回用户的整个记录,这使得很难找到嵌套成员来更新它。
如何将结果修剪为只有具有找到的ID的成员并更新其属性?
我正在使用Mongo 3.6.2并尝试过新的arrayFilters,但没有运气。
我的代码(使用Node)在下面,它返回整个文档,但没有更新。
const query = {
groups : {
$elemMatch : { members : { $elemMatch : {_id : <id>} } }
}
};
const update = {$set: {'groups.$[].members.$[o].hasAccepted':true }};
const options = { new: true, arrayFilters:[{"o._id":<id>}] };
// Find the document
User.findOneAndUpdate(query, update, options, function(error, result) {
if (error) {
res.send(error);
} else {
res.send(result);
}
});
编辑:这是我正在使用的测试数据库的完整数据。我一直在测试的_id是第1组中的成员之一:5a753f168b5b7f0231ab0621
[
{
"_id": {
"$oid": "5a7505452f93de2c90f49a20"
},
"groups": [
{
"name": "Group 2",
"_id": {
"$oid": "5a7543b8e254ab02cd728c42"
},
"members": [
{
"user": {
"$oid": "5a7543b8e254ab02cd728c41"
},
"_id": {
"$oid": "5a7543b8e254ab02cd728c43"
},
"hasAccepted": false
}
]
},
{
"name": "Group 1",
"_id": {
"$oid": "5a753f168b5b7f0231ab0620"
},
"members": [
{
"user": {
"$oid": "5a753f168b5b7f0231ab061f"
},
"_id": {
"$oid": "5a753f168b5b7f0231ab0621"
},
"hasAccepted": false
}
]
}
]
},
{
"_id": {
"$oid": "5a753f168b5b7f0231ab061f"
},
"groups": [],
},
{
"_id": {
"$oid": "5a7543b8e254ab02cd728c41"
},
"groups": [],
}
]
感谢您提供的任何帮助。
答案 0 :(得分:1)
好的,事实证明我需要更好地理解的是arrayFilters(我需要将组名添加到我用来获取我需要更新的值的数据中。
帮助我理解arrayFilters最好的事情就是将其视为一种子查询,就像在SQL世界中使用的一样。一旦我知道了,我就能弄清楚如何编写更新。
本文对理解如何使用arrayFilters非常有帮助:http://thecodebarbarian.com/a-nodejs-perspective-on-mongodb-36-array-filters.html
这里有适合我的代码。请注意,您需要Mongo 3.6和Mongoose 5.0.0才能获得对arrayFilters的支持。
此外,您需要确保要求Mongoose的ObjectId
const ObjectId = require('mongoose').Types.ObjectId;
这是其余的工作代码:
const query = {
groups : {
$elemMatch : { members : { $elemMatch : {_id : new ObjectId("theideofmymemberobject"), hasAccepted : false} } }
}
};
const update = {$set: {'groups.$[group].members.$[member].hasAccepted':true } };
const options = { arrayFilters: [{ 'group.name': 'Group 3' },{'member._id': new ObjectId("theideofmymemberobject")}] };
// update the document
User.update(query, update, options, function(error, result) {
if (error) {
res.send(error);
} else {
res.send(result);
}
});
答案 1 :(得分:0)
You can try below aggregation to filter only the matching group and member from the group and member arrays
replace _id with the id to be searched, use the result to update the hasAccepted status
db.groups.aggregate(
[
{$addFields : {"groups" : {$arrayElemAt : [{$filter : {input : "$groups", as : "g", cond : {$in : [_id, "$$g.members._id"]}}}, 0]}}},
{$addFields : {"groups.members" : {$filter : {input : "$groups.members", as : "gm", cond : {$eq : [_id, "$$gm._id"]}}}}}
]
).pretty()
答案 2 :(得分:0)
// An easier more modern way to do this is to pass a wildcard such as /:id in your API endpoint
// Use Object.assign()
// use the save() method
// If you are using JWT
// Otherwise find another unique identifier
const user = UserSchema.findOne({ id: req.user.id });
for (const oldObject of user.groups) {
if(oldObject.id === req.params.id) {
newObject = {
propertyName: req.body.val,
propertyName2: req.body.val2,
propertyName3: req.body.val3
}
// Update the oldObject
Object.assign(oldObject, newObject);
break;
}
}
user.save()
res.json(user);