124

I have an array in my model document. I would like to delete elements in that array based on a key I provide and then update MongoDB. Is this possible?

Here's my attempt:

var mongoose = require('mongoose'),
    Schema = mongoose.Schema;

var favorite = new Schema({
    cn: String,
    favorites: Array
});

module.exports = mongoose.model('Favorite', favorite, 'favorite');

exports.deleteFavorite = function (req, res, next) {
    if (req.params.callback !== null) {
        res.contentType = 'application/javascript';
    }
    Favorite.find({cn: req.params.name}, function (error, docs) {
        var records = {'records': docs};
        if (error) {
            process.stderr.write(error);
        }
        docs[0]._doc.favorites.remove({uid: req.params.deleteUid});

        Favorite.save(function (error, docs) {
            var records = {'records': docs};
            if (error) {
                process.stderr.write(error);
            }
            res.send(records);

            return next();
        });
    });
};

So far it finds the document but the remove nor save works.

occasl
  • 5,303
  • 5
  • 56
  • 81

9 Answers9

187

You can also do the update directly in MongoDB without having to load the document and modify it using code. Use the $pull or $pullAll operators to remove the item from the array :

Favorite.updateOne({ cn: req.params.name }, {
    $pullAll: {
        favorites: req.params.deleteUid,
    },
});

To remove objects from array then

Favorite.updateOne({ cn: req.params.name }, {
    $pullAll: {
        favorites: [{_id: req.params.deleteUid}],
    },
});

(you can also use updateMany for multiple documents)

http://docs.mongodb.org/manual/reference/operator/update/pullAll/

Rohit Parte
  • 3,365
  • 26
  • 26
Daniel Flippance
  • 7,734
  • 5
  • 42
  • 55
  • 13
    I don't get how this is removing an item from the favorites array (the sub array not the document) without any reference to the name favorites – Dominic Feb 13 '18 at 21:46
  • 1
    Also this operation is atomic. Meaning that the document will not have the potential to change between separate find and update operations. – James Sep 07 '18 at 17:49
  • 2
    @Dominic The `favourites` is now added. I can't believe this went unnoticed for more than 6 years – Parzh from Ukraine Dec 02 '21 at 16:01
99

The checked answer does work but officially in MongooseJS latest, you should use pull.

doc.subdocs.push({ _id: 4815162342 }) // added
doc.subdocs.pull({ _id: 4815162342 }) // removed

https://mongoosejs.com/docs/api.html#mongoosearray_MongooseArray-pull

I was just looking that up too.

See Daniel's answer for the correct answer. Much better.

LondonRob
  • 73,083
  • 37
  • 144
  • 201
King Friday
  • 25,132
  • 12
  • 90
  • 84
16

Answers above are shown how to remove an array and here is how to pull an object from an array.

Reference: https://docs.mongodb.com/manual/reference/operator/update/pull/

db.survey.update( // select your doc in moongo
    { }, // your query, usually match by _id
    { $pull: { results: { $elemMatch: { score: 8 , item: "B" } } } }, // item(s) to match from array you want to pull/remove
    { multi: true } // set this to true if you want to remove multiple elements.
)
Mike Musni
  • 171
  • 1
  • 3
  • Please be careful when using this example, it might not work in some case as mentioned in the doc above `In fact, the following operation will not pull any element from the original collection.` . Read the doc above for more detail. – cjmling May 02 '21 at 04:47
8

Since favorites is an array, you just need to splice it off and save the document.

var mongoose = require('mongoose'),
    Schema = mongoose.Schema;

var favorite = new Schema({
    cn: String,
    favorites: Array
});

module.exports = mongoose.model('Favorite', favorite);

exports.deleteFavorite = function (req, res, next) {
    if (req.params.callback !== null) {
        res.contentType = 'application/javascript';
    }
    // Changed to findOne instead of find to get a single document with the favorites.
    Favorite.findOne({cn: req.params.name}, function (error, doc) {
        if (error) {
            res.send(null, 500);
        } else if (doc) {
            var records = {'records': doc};
            // find the delete uid in the favorites array
            var idx = doc.favorites ? doc.favorites.indexOf(req.params.deleteUid) : -1;
            // is it valid?
            if (idx !== -1) {
                // remove it from the array.
                doc.favorites.splice(idx, 1);
                // save the doc
                doc.save(function(error) {
                    if (error) {
                        console.log(error);
                        res.send(null, 500);
                    } else {
                        // send the records
                        res.send(records);
                    }
                });
                // stop here, otherwise 404
                return;
            }
        }
        // send 404 not found
        res.send(null, 404);
    });
};
Stunner
  • 12,025
  • 12
  • 86
  • 145
Deathspike
  • 8,582
  • 6
  • 44
  • 82
  • Thank you for a very thorough response. – occasl Feb 08 '13 at 18:10
  • 4
    if you try to remove 2 things at the same time, they both might get the array, then change it, and the first change that is saved gets overwriten by the second. I would rather go with sth like this http://stackoverflow.com/questions/16959099/how-to-remove-array-element-in-mongodb . – Maximosaic Oct 25 '13 at 15:03
4

This is working for me and really very helpful.

SubCategory.update({ _id: { $in:
        arrOfSubCategory.map(function (obj) {
            return mongoose.Types.ObjectId(obj);
        })
    } },
    {
        $pull: {
            coupon: couponId,
        }
    }, { multi: true }, function (err, numberAffected) {
        if(err) {
            return callback({
                error:err
            })
        }
    })
});

I have a model which name is SubCategory and I want to remove Coupon from this category Array. I have an array of categories so I have used arrOfSubCategory. So I fetch each array of object from this array with map function with the help of $in operator.

monkeyinsight
  • 4,719
  • 1
  • 20
  • 28
coder
  • 540
  • 1
  • 5
  • 17
2
keywords = [1,2,3,4];
doc.array.pull(1) //this remove one item from a array
doc.array.pull(...keywords) // this remove multiple items in a array

if you want to use ... you should call 'use strict'; at the top of your js file; :)

crazy_phage
  • 550
  • 9
  • 28
2

I used this format for my project and it's worked

router.delete('/dashboard/participant/:id', async (req, res, next) => {
    try {
        const participant = await Participant.findByIdAndDelete({ _id: req.params.id });
        // { $pull: { templates: { _id: templateid } } },
        const event = await Event.findOneAndUpdate({ participants: participant._id }, { $pull: { participants: participant._id } }, { new: true });
        res.status(200).json({ request: 'Deleted', participant, event });
    } catch (error) {
        res.json(error)
    }
});
MD SHAYON
  • 7,001
  • 45
  • 38
0
Favorite.update({ cn: req.params.name }, { "$pull": { "favorites": { "_id": favoriteId } }}, { safe: true, multi:true }, function(err, obj) {
    //do something smart
});
Cristian Zumelzu
  • 842
  • 10
  • 15
  • 3
    Your answer could be improved by adding more information on what the code does and how it helps the OP. – Tyler2P Feb 27 '22 at 11:28
  • Is pretty simple to read if you read the question and then check my code ;) – Cristian Zumelzu Mar 01 '22 at 16:47
  • 1
    Still needs an explanation. – asciidude Oct 09 '22 at 23:50
  • multi: update multiple documents at once. safe: verify that the update (write) was performed then you have the update query, "cn" is the field so you will update only that ones that the names will be equal to "req.params.name" value (In node you get query params like req.params). Then in the other part of the update you have to specify what you want to update. So basically the pull means that you will operate and remove a value over an array, the value is a subobject that have a property "favorites" – Cristian Zumelzu Oct 10 '22 at 11:21
  • this favorites is an array of objects from other collection Favorite { ... favorites:[{ _id: ObjectId("") }] .. – Cristian Zumelzu Oct 10 '22 at 11:28
0

This is a sample in TypeScript using Mongoose.

// Each company has COI
// COI looks like { buildingId: string, name: string, file: string }
// Company is an Entity Company looks like { companyId: string, cois: COI[] }

function deleteCOI(companyId: string, buildingId: string) {
  const company = await Companies.findOneAndUpdate(
    { companyId },
    { $pull: { cois: { buildingId: buildingId } } }
  );
  return company;
}
Itay Ben Shmuel
  • 648
  • 7
  • 9