我在MongoDB和Mongoose中遇到了一些认为微不足道的问题。
用这样一个相当简单的模式
const UserSchema = new Schema({
groups: [
{
name: String,
members: [
{
hasAccepted: {
type: Boolean
}
}
]
}
]
});
当我创建新组时,每个成员对象当然都有一个_id的属性。我只想通过它的_id选择该成员并更新它的has接受属性。
当我用成员的_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": [],
}
]
谢谢你能提供的任何帮助。
好的,所以事实证明我需要更好地理解的是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);
}
});
可以尝试下面的聚合,从组和成员数组中只筛选匹配的组和成员
将_id替换为要搜索的id,使用结果更新has可接受状态
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()
// 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);