Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

mongoose - how do I get the elements removed from $pull

I'm using $pull to pull a subdocument within an array of a document. Don't know if it matters but in my case the subdocuments contain _id so they are indexed.
Here are JSONs that describes the schemas:

user: {
  _id: String,
  items: [UserItem]
}
UserItem: {
  _id: String,
  score: Number
}

Now my problem is this: I am using $pull to remove certain UserItem's from a User.

var delta = {$pull:{}};
delta.$pull.items={};
delta.$pull.items._id = {$in: ["mongoID1", "mongoID2" ...]};
User.findOneAndUpdate(query, delta, {multi: true}, function(err, data){
    //whatever...
});

What i get in data here is the User object after the change, when what i wish to get is the items that were removed from the array (satellite data). Can this be done with one call to the mongo or do I have to do 2 calls: 1 find and 1 $pull?
Thanks for the help.

like image 499
Tzali Maimon Avatar asked Jan 18 '26 10:01

Tzali Maimon


1 Answers

You really cannot do this, or at least there is nothing that is going to return the "actual" elements that were "pulled" from the array in any response, even with the newer WriteResponse objects available to the newer Bulk Operations API ( which is kind of the way forward ).

The only way you can really do this is by "knowing" the elements you are "intending" to "pull", and then comparing that to the "original" state of the document before it was modified. The basic MongoDB .findAndModify() method allows this, as do the mongoose wrappers of .findByIdAndUpdate() as well and .findOneAndUpdate().

Basic usage premise:

var removing = [ "MongoId1", "MongoId2" ];

Model.findOneAndUpdate(
    query,
    { "$pull": { "items._id": { "$in": removing } } },
    { "new": false },
    function(err,doc) {
        var removed = doc.items.filter(function(item) {
           return removing.indexOf(item) != -1;
        });

        if ( removed.length > 0 )
           console.log(removed);
    }
);

Or something along those lines. You basically "turn around" the default mongoose .findOneAndUpdate() ( same for the other methods ) behavior and ask for the "original" document before it was modified. If the elements you asked to "pull" were present in the array then you report them, or inspect / return true or whatever.

So the mongoose methods differ from the reference implementation by returning the "new" document by default. Turn this off, and then you can "compare".

Further notes: "multi" is not a valid option here. The method modifies "one" document by definition. Also you make a statement that the array sub-documents conatain an _id. This is done by "mongoose" by default. But those _id values in the array are "not indexed" unless you specifically define an index on that field. The only default index is the "primary document" _id.

like image 88
Neil Lunn Avatar answered Jan 19 '26 23:01

Neil Lunn



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!