In mongoDb, how do you remove an array element by its index?

In the following example, assume the document is in the db.people collection.

How to remove the 3rd element of the interests array by it's index?

{
  "_id" : ObjectId("4d1cb5de451600000000497a"),           
  "name" : "dannie",  
  "interests" : [  
    "guitar",  
    "programming",           
    "gadgets",  
    "reading"  
  ]   
}

This is my current solution:

var interests = db.people.findOne({"name":"dannie"}).interests;  
interests.splice(2,1)  
db.people.update({"name":"dannie"}, {"$set" : {"interests" : interests}});

Is there a more direct way?


Solution 1:

There is no straight way of pulling/removing by array index. In fact, this is an open issue http://jira.mongodb.org/browse/SERVER-1014 , you may vote for it.

The workaround is using $unset and then $pull:

db.lists.update({}, {$unset : {"interests.3" : 1 }}) 
db.lists.update({}, {$pull : {"interests" : null}})

Update: as mentioned in some of the comments this approach is not atomic and can cause some race conditions if other clients read and/or write between the two operations. If we need the operation to be atomic, we could:

  • Read the document from the database
  • Update the document and remove the item in the array
  • Replace the document in the database. To ensure the document has not changed since we read it, we can use the update if current pattern described in the mongo docs

Solution 2:

You can use $pull modifier of update operation for removing a particular element in an array. In case you provided a query will look like this:

db.people.update({"name":"dannie"}, {'$pull': {"interests": "guitar"}})

Also, you may consider using $pullAll for removing all occurrences. More about this on the official documentation page - http://www.mongodb.org/display/DOCS/Updating#Updating-%24pull

This doesn't use index as a criteria for removing an element, but still might help in cases similar to yours. IMO, using indexes for addressing elements inside an array is not very reliable since mongodb isn't consistent on an elements order as fas as I know.

Solution 3:

in Mongodb 4.2 you can do this:

db.example.update({}, [
     {$set: {field: {
           $concatArrays: [ 
                  {$slice: ["$field", P]}, 
                  {$slice: ["$field", {$add: [1, P]}, {$size: "$field"}]}
           ]
     }}}
]);

P is the index of element you want to remove from array.

If you want to remove from P till end:

db.example.update({}, [
     {$set: {field: {$slice: ["$field", P]}}
]);

Solution 4:

Rather than using the unset (as in the accepted answer), I solve this by setting the field to a unique value (i.e. not NULL) and then immediately pulling that value. A little safer from an asynch perspective. Here is the code:

    var update = {};
    var key = "ToBePulled_"+ new Date().toString();
    update['feedback.'+index] = key;
    Venues.update(venueId, {$set: update});
    return Venues.update(venueId, {$pull: {feedback: key}});

Hopefully mongo will address this, perhaps by extending the $position modifier to support $pull as well as $push.