'MongoDB: Find the minimum element in array and delete it

I have a documents in MongoDB, one of them looks like this:

{
"_id" : 100,
"name" : "Something",
"items" : [
    {
        "item" : 47,
        "color" : "red"
    },
    {
        "item" : 44,
        "color" : "green"
    },
    {
        "item" : 39,
        "color" : "blue"
    }
]
}

In every document I need to find the minimum item and delete it. So it should be like this:

{
"_id" : 100,
"name" : "Something",
"items" : [
    {
        "item" : 47,
        "color" : "red"
    },
    {
        "item" : 44,
        "color" : "green"
    }
]
}

It looks like findAndModify function should be used here but I can't go any further.

How to find the minimum element in array and delete it?

I'm using MongoDB and Pymongo driver.



Solution 1:[1]

If you are not restricted to having the query be in one single step, you could try:

step 1) use the aggregate function with the $unwind and $group operators to find the minimum item for each document

myresults = db.megas.aggregate( [ { "$unwind": "$items" },  
    {"$group": { '_id':'$_id' , 'minitem': {'$min': "$items.item" } } } ] )

step 2) the loop through the results and $pull the element from the array

for result in myresults['result']:
    db.megas.update( { '_id': result['_id'] }, 
        { '$pull': { 'items': { 'item': result['minitem'] } } } )

Solution 2:[2]

Please find my solution written in plain javascript. It should work straight through MongoDb shell

cursor = db.students.aggregate(
[{ "$unwind": "$items" }, 
 { "$match": { "items.color": "green"}},
 { "$group": {'_id': '$_id', 'minitem': {
                '$min': "$items.item"
            }
        }
    }

]);

cursor.forEach(
function(coll) {
    db.students.update({
        '_id': coll._id
    }, {
        '$pull': {
            'items': {
                'item': coll.minitem
            }
        }
    })
})

Solution 3:[3]

Starting in Mongo 4.4, the $function aggregation operator allows applying a custom javascript function to implement behaviour not supported by the MongoDB Query Language.

And coupled with improvements made to db.collection.update() in Mongo 4.2 that can accept an aggregation pipeline, allowing the update of a field based on its own value,

We can manipulate and update an array in ways the language doesn't easily permit:

// {
//   "name" : "Something",
//   "items" : [
//     { "item" : 47, "color" : "red"   },
//     { "item" : 39, "color" : "blue"  },
//     { "item" : 44, "color" : "green" }
//   ]
// }
db.collection.update(
  {},
  [{ $set:
    { "items":
      { $function: {
          body: function(items) {
            var min = Math.min(...items.map(x => x.item));
            return items.filter(x => x.item != min);
          },
          args: ["$items"],
          lang: "js"
      }}
    }
  }],
  { multi: true }
)
// {
//   "name" : "Something",
//   "items" : [
//     { "item" : 47, "color" : "red"   },
//     { "item" : 44, "color" : "green" }
//   ]
// }

$function takes 3 parameters:

  • body, which is the function to apply, whose parameter is the array to modify. The function here simply consists in first finding the minimum item from the array in order to then filter it out.
  • args, which contains the fields from the record that the body function takes as parameter. In our case, "$items".
  • lang, which is the language in which the body function is written. Only js is currently available.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 megas
Solution 2 juanreyesv
Solution 3