Mongodb 通过Mongoose.js中的ObjectId选择和更新嵌套对象

Mongodb 通过Mongoose.js中的ObjectId选择和更新嵌套对象,mongodb,mongoose,Mongodb,Mongoose,我在MongoDB和Mongoose中遇到了一些被认为是微不足道的问题 使用这样一个相当简单的模式 const UserSchema = new Schema({ groups: [ { name: String, members: [ { hasAccepted: { type: Boolean } }

我在MongoDB和Mongoose中遇到了一些被认为是微不足道的问题

使用这样一个相当简单的模式

const UserSchema = new Schema({
groups: [
    {
        name: String,
        members: [
            {
                hasAccepted: {
                    type: Boolean
                }
            }
        ]
    }
]
});
当我创建新组时,每个成员对象当然都会获得一个_id属性。我只想通过其_id选择该成员并更新其hasAccept属性

当我使用成员的_id运行查询时,我会为用户返回整个记录,这使得很难找到嵌套成员来更新它

如何将结果精简为仅包含找到的ID的成员并更新其属性

我正在使用Mongo 3.6.2,并尝试了新的ArrayFilter,但运气不佳

下面是我使用节点的代码,它返回整个文档,但没有更新任何内容

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替换为要搜索的id,使用结果更新hasAccepted状态

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()

好的,我需要更好地理解的是数组过滤器,我需要将组名添加到数据中,以获得需要更新的值

帮助我更好地理解arrayFilters的一件事是将其视为一种子查询,就像SQL世界中使用的一样。一旦我得到了,我就能够想出如何写我的更新

这篇文章对于理解如何使用ArrayFilter也很有帮助:

这是对我有用的代码。请注意,您需要Mongo3.6和Mongoose 5.0.0才能获得对ArrayFilter的支持

另外,您需要确保像这样要求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?我知道名称,是的。这有帮助吗?好吧,我还没有真正使用聚合技术,但只有几周的时间使用Mongo本身,所以我不完全理解代码。当我在替换真实objectid后从mongo提示符运行该命令时,我没有得到任何返回-没有错误,没有结果,只是再次得到命令提示符。ok np,您可以发布查询和您尝试过的示例文档吗?您还可以发布您执行的查询吗?这是我根据您的答案使用的:db.groups.aggregate[{$addFields:{groups:{$arrayElemAt:[{$filter:{$groups,as:g,cond:{$in:[5A753F168B7F0231AB0621,$$g.members.{$id]}}}},0]}},{$addFields:{groups.members:{$filter:{input:$groups.members,as:gm,cond:{$eq:[5A753F168B5B5F0231AB0621,$$gm.{$id]}}能否尝试在查询中使用ObjectD5A753B553B17B53B7F0231AB0621而不是字符串?
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);
    }
});
// 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);