【问题标题】:Selecting and updating a nested object by it's ObjectId in Mongoose.js通过 Mongoose.js 中的 ObjectId 选择和更新嵌套对象
【发布时间】:2018-07-13 15:37:41
【问题描述】:

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

使用这样一个相当简单的架构

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": [],

}
]

感谢您提供的任何帮助。

【问题讨论】:

  • 你知道会员所属的群组名称还是只有会员的_id
  • 我知道名字,是的。这有帮助吗?

标签: mongodb mongoose


【解决方案1】:
// 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);

【讨论】:

    【解决方案2】:

    好的,事实证明我需要更好地理解的是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);
        }
    });
    

    【讨论】:

      【解决方案3】:

      您可以尝试以下聚合以仅从组和成员数组中过滤匹配的组和成员

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

      【讨论】:

      • 好的,我还没有真正使用过聚合(只有几周使用 Mongo 本身),所以我不完全理解代码。当我在替换一个真正的 objectid 后从 mongo 提示符运行它时,我什么也没返回 - 没有错误,没有结果,只是再次出现命令提示符。
      • ok np,您可以发布查询和您尝试过的示例文档吗?
      • 您也可以发布您执行的查询吗?
      • 这是我根据你的回答使用的: db.groups.aggregate( [ {$addFields : {"groups" : {$arrayElemAt : [{$filter : {input : "$groups" , as : "g", cond : {$in : ["5a753f168b5b7f0231ab0621", "$$g.members._id"]}}}, 0]}}}, {$addFields : {"groups.members" : { $filter : {input : "$groups.members", as : "gm", cond : {$eq : ["5a753f168b5b7f0231ab0621", "$$gm._id"]}}}}} ])
      • 你能在查询中用ObjectId("5a753f168b5b7f0231ab0621")代替字符串id吗?
      猜你喜欢
      • 1970-01-01
      • 2013-04-30
      • 2021-12-26
      • 2019-02-20
      • 2014-05-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多