Yash Kapila
Yash Kapila

Reputation: 271

Mongoose - increment a field value in nested array

I'm new to Mongoose and MongoDB and am trying to implement a scenario where I have to update a field value residing in a nested array.

For example, my DB entry is as follows:

{
    "_id" : ObjectId("581999ecf97860b77c1625f6"),      // this is userID
    "username" : "test",
    "__v" : 0,
    "polls" : [
            {
                    "url" : "xxxx",
                    "question" : "city or united",
                    "_id" : ObjectId("581999ec3ba2b15ad7fbcd50"),    //this is questionID
                    "option" : [
                            {
                                    "votes" : 0,
                                    "value" : "city",
                                    "_id" : ObjectId("581999ec3ba2b15ad7fbcd52")    //this is optionID
                            },
                            {
                                    "votes" : 0,
                                    "value" : "united",
                                    "_id" : ObjectId("581999ec3ba2b15ad7fbcd51")    //this is optionID
                            }
                    ]
            },
            {
                    "url" : "yyyy",
                    "question" : "chelsea or arsenal",
                    "_id" : ObjectId("58199d0ef11835685d3f41b7"),
                    "option" : [
                            {
                                    "votes" : 0,
                                    "value" : "chelsea",
                                    "_id" : ObjectId("58199d0ef11835685d3f41b9")
                            },
                            {
                                    "votes" : 0,
                                    "value" : "arsenal",
                                    "_id" : ObjectId("58199d0ef11835685d3f41b8")
                            }
                    ]
            }
    ]
}

I need to increment the votes of a particular option whenever it is selected from the front-end.

Below is what I've tried to implement so far but without success.

PollsSchema.methods.submitVote = function(id, callback){
var increment = { $inc: { 'polls.option.$.votes': 1 } };
var query = { 
    '_id': id.userID,
    'polls._id': id.questionID, 
    'polls.option._id': id.optionID
};

return this.model('Poll').update(query, increment, callback);
};

Below is my schema:

var PollsSchema = new Schema({
username: String,
polls: [
    {
        question: String,
        option: [
            {
                value: String,
                votes: Number
            }
        ],
        url: String
    }
]
});

Is there something that I'm doing incorrectly over here?

Thank you for your time and suggestions.

Upvotes: 0

Views: 2137

Answers (1)

s7vr
s7vr

Reputation: 75924

You've the hit the Mongo db limitation where it doesn't support the nested array placeholder replacement. The positional operator can only be used for one level. So if you were to update the question like polls.$.question under polls, it will work fine. Also, mongo db doesnt have support for multiple placeholder something like polls.$.option.$.votes.

As pointed in the comment you can view that jira defect. Now coming to a workaround.

You can flatten your options so we can have it inside the polls. Something like this below. As long as you have unique id for question and option you should always can resolve to unique record.

{
    "_id": ObjectId("581999ecf97860b77c1625f6"), // this is userID
    "username": "test",
    "__v": 0,
    "polls": [{
        "url": "xxxx",
        "question": "city or united",
        "_id": ObjectId("581999ec3ba2b15ad7fbcd50"), //this is questionID
        "votes": 0,
        "value": "city",
        "option_id": ObjectId("581999ec3ba2b15ad7fbcd52") //this is optionID
    }, {
        "url": "xxxx",
        "question": "city or united",
        "_id": ObjectId("581999ec3ba2b15ad7fbcd50"), //this is questionID
        "votes": 0,
        "value": "united",
        "option_id": ObjectId("581999ec3ba2b15ad7fbcd51") //this is optionID
    }]
}

Now you can use something like this to increment the votes.

PollsSchema.methods.submitVote = function(id, callback) {
    var increment = {
        $inc: {
            'polls.$.votes': 1
        }
    };
    var query = {
        '_id': id.userID,
        'polls._id': id.questionID,
        'polls.option_id': id.optionID
    };

    return this.model('Poll').update(query, increment, callback);
};

Other option is to use aggregate pipeline and find the item and then update that returned object.

Upvotes: 1

Related Questions