1

Environment

Native Node driver
MongoDB 4.0.2

Desired Behaviour

Delete an object from an array of objects based on its id, and decrement other object's position value where applicable.

Specifically, I am trying to:

iterate over an array of objects in MongoDB and update a property in each object

Perhaps it might involve using $inc and -1 , but i'm still unsure how to do the iteration.

What I've Tried

I have the logic working for a JavaScript scenario (works in browser console):

function remove_and_update_position(id) {

    // faux array in collection document
    var statements = [{
            position: 0,  // < i want to update 'position' of all objects after an object is removed  
            id: "a"
        },
        {
            position: 1,
            id: "b"
        },
        {
            position: 2,
            id: "c"
        },
        {
            position: 3,
            id: "d"
        },
        {
            position: 4,
            id: "e"
        }

    ];

    // delete an object from the 'statements' array by its id
    statements.splice(statements.indexOf(statements.find(item => item.id == id)), 1);

    // iterate over the modified array
    for (var i = 0; i < statements.length; i++) {

        // for all objects in the modified array,  
        // with position numbers greater than the  
        // position removed ( minus 1),  
        // decrement their position numbers
        if (i > position - 1) {
            var new_position = statements[i].position - 1;
            statements[i].position = new_position;
        }

    }

    // verify adjustments were correct
    console.log(statements);

}

// call the function
remove_and_update_position("c");

I just need to translate it into a MongoDB/Node scenario - so far i just have the object removal part:

const remove_and_update_position = (req, res) => {

    var request_body = req.body;

    var topic_id = request_body.topic_id;

    var o_id = new ObjectID(topic_id);

    var collection = mongo_client.db("topics").collection("topics");

    var statement_id = request_body.statement_id; 

    var position = Number(request_body.position); 

    // query for the relevant document *and* target nested statement by id
    var filter = { _id: o_id, "statements.id": statement_id };

    // remove from statements array
    var update = { $pull: { "statements": { id: statement_id } } };

    // perform the update
    collection.updateOne(filter, update, function(err, doc) {
        if (err) {
            res.send(err);
        } else {
            res.json({ response_type: "success" });
        }
    });

}
user1063287
  • 10,265
  • 25
  • 122
  • 218

1 Answers1

1

Updated per answer here.

const remove_and_update_position = (req, res) => {

    var request_body = req.body;

    var topic_id = request_body.topic_id;

    var o_id = new ObjectID(topic_id);

    var collection = mongo_client.db("topics").collection("topics");

    var statement_id = request_body.statement_id;

    var position = Number(request_body.position);

    // query for the relevant document *and* target nested statement by id
    var filter = { _id: o_id, "statements.id": statement_id };

    // remove from statements array
    var update = { $pull: { "statements": { id: statement_id } } };

    // perform the update
    collection.updateOne(filter, update, function(err, doc) {
        if (err) {
            res.send(err);
        } else {

            // BEGIN decrement others objects position
            var position_indicator = position - 1;

            console.log("deleted object position: " + position);
            console.log("will decrement objects with position greater than: " + position_indicator);

            var new_filter = { _id: o_id, "statements.position": { $gt: position_indicator } };

            var new_update = { $inc: { "statements.$[elem].position": -1 } };

            var array_filters = { "arrayFilters": [{ "elem.position": { $gt: position_indicator } }], "multi": true }; 

            collection.updateOne(new_filter, new_update, array_filters, function(err, doc) {
                if (err) {
                    res.send(err);
                } else {
                    res.json({ response_type: "success" });
                }
            });
            // END decrement others objects position
        }
    });

}
user1063287
  • 10,265
  • 25
  • 122
  • 218
tashakori
  • 2,331
  • 1
  • 22
  • 29
  • Would the `position` property in `updateMany` need to be something like: `statements.position`? Because the object is nested in the `statements` array? – user1063287 Sep 10 '18 at 06:48
  • Hmm, i think this is only updating one object in the `statements` array. The [updateMany() docs](https://docs.mongodb.com/manual/reference/method/db.collection.updateMany/) do say this method `updates multiple documents within the collection based on the filter`. So maybe this is not the correct method to use as it doesn't seem to update multiple *objects* within an array. Maybe I just need to do some sort of standard `update()` where some sort of `multi` is defined as true? However [this answer](https://stackoverflow.com/a/4669702) makes it seem it is not possible? :/ – user1063287 Sep 10 '18 at 07:35
  • Hmm, that seems to make the query ignore `$gt` and *all* objects have their position updated (even object at position `0` is changed to position `-1`, if object at position `2` is removed). I am looking at this updated answer now: https://stackoverflow.com/a/46054172 ... have updated your answer with what is working for me. – user1063287 Sep 10 '18 at 08:49