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

时间:2018-02-03 06:07:40

标签: mongodb mongoose

我遇到麻烦的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": [],

}
]

感谢您提供的任何帮助。

3 个答案:

答案 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);