Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
1.7k views
in Technique[技术] by (71.8m points)

mongodb - Selecting and updating a nested object by it's ObjectId in Mongoose.js

I'm having trouble with something that thought would be trivial in MongoDB with Mongoose.

With a fairly simple schema like this

const UserSchema = new Schema({
groups: [
    {
        name: String,
        members: [
            {
                hasAccepted: {
                    type: Boolean
                }
            }
        ]
    }
]
});

When i create new groups, each member object gets an _id property of course. I simply want to select that member by its _id and update its hasAccepted property.

When I run a query with the _id of the member, I get back the entire record for the user, which makes it difficult to find the nested member to update it.

How can I trim the result down to just the member with the found ID and update its property?

I'm using Mongo 3.6.2 and have tried the new arrayFilters, but with no luck.

My code (using Node) is below, which returns the whole document, but with nothing updated.

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);
    }
});

EDIT: here's the full data from the test db i'm working with. The _id I've been testing with is one the for the member in Group 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": [],

}
]

Thanks for any help you can offer.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

OK, so it turns out the the thing I needed to understand better are arrayFilters (that, and I needed to add the group name into the data I used to get to the value I needed to updated.

The thing that helped me understand arrayFilters the best was to think of the as a sort of subquery, like is used in the SQL world. Once I got that, I was able to figure out how to write my update.

This article was also very helpful in understanding how arrayFilters are used: http://thecodebarbarian.com/a-nodejs-perspective-on-mongodb-36-array-filters.html

Here's the code that worked for me. Note that you need Mongo 3.6 and Mongoose 5.0.0 to get support for arrayFilters.

Also, you need to be sure to require Mongoose's ObjectId like so

const ObjectId = require('mongoose').Types.ObjectId;

Here's the rest of the working code:

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);
    }
});

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...