How to pull one instance of an item in an array in MongoDB?

mongodb, mongodb-query, node.js

Solution

So you are correct in that the `$pull` operator does exactly what the documentation says in that it's arguments are in fact a "query" used to match the elements that are to be removed.

If your array content happened to always have the element in the "first" position as you show then the `$pop` operator does in fact remove that first element.

With the basic node driver:

collection.findOneAndUpdate(
    { "array.0": "bird" },       // "array.0" is matching the value of the "first" element 
    { "$pop": { "array": -1 } },
    { "returnOriginal": false },
    function(err,doc) {

    }
);

With mongoose the argument to return the modified document is different:

MyModel.findOneAndUpdate(
    { "array.0": "bird" },
    { "$pop": { "array": -1 } },
    { "new": true },
    function(err,doc) {

    }
);

But neither are of much use if the array position of the "first" item to remove is not known.

For the general approach here you need "two" updates, being one to match the first item and replace it with something unique to be removed, and the second to actually remove that modified item.

This is a lot more simple if applying simple updates and not asking for the returned document, and can also be done in bulk across documents. It also helps to use something like async.series in order to avoid nesting your calls:

async.series(
    [
        function(callback) {
            collection.update(
                { "array": "bird" },
                { "$unset": { "array.$": "" } },
                { "multi": true }
                callback
            );
        },
       function(callback) {
           collection.update(
                { "array": null },
                { "$pull": { "array": null } },
                { "multi": true }
                callback
           );
       }
    ],
    function(err) {
       // comes here when finished or on error   
    }
);

So using the `$unset` here with the positional `$` operator allows the "first" item to be changed to `null`. Then the subsequent query with `$pull` just removes any `null` entry from the array.

That is how you remove the "first" occurance of a value safely from an array. To determine whether that array contains more than one value that is the same though is another question.

Problem

According to the documents: The $pull operator removes from an existing array all instances of a value or values that match a specified condition. Is there an option to remove only the first instance of a value? For example: ``` var array = ["bird","tiger","bird","horse"] ``` How can the first "bird" be removed directly in an update call?

Original source