[SERVER-1534] $min, $max field update operators Created: 02/Aug/10  Updated: 27/Oct/15  Resolved: 28/Sep/13

Status: Closed
Project: Core Server
Component/s: Write Ops
Affects Version/s: None
Fix Version/s: 2.5.3

Type: New Feature Priority: Minor - P4
Reporter: Stefan Kaes Assignee: Scott Hernandez
Resolution: Fixed Votes: 38
Labels: None

Issue Links:
depends on SERVER-6399 Refactor update() code Closed
related to DOCS-2012 $min/$max update operators Closed
Backwards Compatibility: Fully Compatible


Add $min/max update modifiers which leave/replace the existing field based on the comparison of a provided value.

Note: if the field is missing then the value supplied will replace it like it was specified via a $set.


> db.v.find()
{_id:1, v:1}
{_id:2, v:2}
> db.v.update({_id:1}, {$min: {v: 2}} // do not change the doc, existing value is smaller
> db.v.update({_id:1}, {$min: {v: 0}} // set v to 0
> db.v.findOne({_id:1})
{_id:1, v:0}
> db.v.update({_id:2}, {$max: {v: 1}} // do not change the doc, existing value is larger
> db.v.update({_id:2}, {$max: {v: 2}} // do not change the doc, existing value is the same
> db.v.update({_id:2}, {$max: {v: 200}} // set v to 200
> db.v.findOne({_id:2})
{_id:2, v:200}
> db.v.update({_id:3}, {$min: {v: 2}} // set v to 2
{_id:3, v:2}
> db.v.update({_id:4}, {$max: {v: 2}} // set v to 2
{_id:4, v:2}

old description
I'm working on a realtime website performance analysis tool, were a number of parallel processes import data into a mongodb instance.

In this context, it would be extremely helpful to have $max and $min update operators which would work similar to $inc.

{ $min :

{ field : value }


sets field to new value if field does not exist or value is smaller than current field value.

{ $max :

{ field : value }


sets field to new value if field does not exist or value is larger than current field value.

Comment by Luke Ehresman [ 18/Oct/10 ]

I would find this very helpful too. Currently I'm poorly simulating it by pushing values into an array and selecting the min value at query time. It would be much more efficient to maintain this at update-time.

Comment by Paul Sadauskas [ 30/Jan/11 ]

I've taken a stab at implementing this feature. https://github.com/mongodb/mongo/pull/21

Works for me, and includes tests. I'm fairly new to C/C++, so I'm open to suggestions for improvements.

Comment by Eliot Horowitz [ 30/Jan/11 ]

We're probably not going to add any more operators until we clean some of that code up.
Also, I think you need to test when there is an index on the field you're changing.
I think its broken for that case (which also appears for other reasons).

Comment by Paul Sadauskas [ 30/Jan/11 ]

You are correct about it not working with indexes. I don't know anything about mongo internals, what do I need to do to make that work?

Too bad about not adding the operators, I could really use them for a project I'm working on, and was hoping to get them in for the next unstable branch (1.9?)

Comment by Eliot Horowitz [ 30/Jan/11 ]

Take a look at the other operators like $inc and $set.

Comment by Paul Sadauskas [ 30/Jan/11 ]

I'm doing exactly the same thing as Mod::SET. Unless it has something to do with this `void Mod::appy` function, but I can't figure out what it does. Are you on IRC #mongodb now?

Comment by Eliot Horowitz [ 30/Jan/11 ]

yes, you have to implement it for $set. Not on irc.

Comment by Paul Sadauskas [ 30/Jan/11 ]

Ok, I got this working. Here's the changes that let it work on indexed fields: https://github.com/paul/mongo/commit/8250f509af8cb8b1ec1c228d1c4a3409523c9df0

The problem is, I think this will perform the update to the same value when the new value is less/greater than the existing one. If I don't do this, however, the value gets removed. Eg `{_id: 1, val: 2}` becomes `{_id: 1}`. I'd rather just not modify the record at all, but there doesn't seem to be any way to do that.

Anyways, what I have here works, and passes the tests.

Comment by Hendrik Schumacher [ 09/Feb/11 ]

Great idea. I applied this patch and it is very useful for my project since it avoids unnecessary queries.
I discovered a bug with $min though: if an entry doesn't exist $min sets it to null and not to the supplied value.

Comment by Kalle Gustafsson [ 13/Apr/11 ]

This would be a very useful feature. We have been discussing this with Kyle Banker during consulting sessions, and he agreed it would be useful in many use cases. Especially when using MongoDB for aggregation purposes.

Comment by Adam Fields [ 14/Apr/11 ]

It would be really nice if this could work for date/time fields, too.

Comment by Grégoire Seux [ 01/Jun/11 ]

Great idea, it would be very good if it was merged into the main branch !

Comment by Dick Mays [ 21/Jul/11 ]

Paul, thanks for tackling this. I'm going to look it over to get it into the main branch.

Comment by Paul Sadauskas [ 02/Sep/11 ]

Any chance we could get this looked at for 2.0?

Comment by Eliot Horowitz [ 02/Sep/11 ]

@paul - no 2.0 is closed for features at this point

Comment by Frank Tyler [ 08/Aug/12 ]

Is this feature still be considered for a future release?

Comment by Eliot Horowitz [ 08/Aug/12 ]

Yes - note "fix version" above.
Just not sure when.

Comment by Walt Woods [ 21/Sep/12 ]

Added a vote, my use case is dates... for instance, I have several services participating in a general "'we' are available until", and would like to just use the maximum availability when updating the records.

Comment by Shane R. Spencer [ 07/Feb/13 ]

Upvoted. I have to update specific ranges only and would appreciate using an index to do so.

Comment by Jordan Stout [ 13/Mar/13 ]

Getting this feature would make the world a better place.

Comment by Kevin J. Rice [ 26/Mar/13 ]

with a doc:

{ _id:xxx, keyField:7, startDate: 20130310, vals:[3,4]}

During a findAndModify, I have to do an upsert and the resultant record is missing startDate. That is, I'm finding based on keyField, I'm updating by $push'ing onto vals, setting upsert=True. Result rec doesn't have startDate! I don't want to modify it if it's already there, so I'd like to update {..., startDate: { $min:

{ $curval, 0}


That, or be able to specify the exact value of the new doc during an upsert in findAndModify().

Comment by Scott Hernandez [ 29/Mar/13 ]

Kevin, you can do things on the insert case using $setOnInsert (http://docs.mongodb.org/manual/reference/operator/setOnInsert/).

Comment by Kevin J. Rice [ 03/Apr/13 ]

Thanks, didn't know about that one! This is the first I've heard of it...

On Fri, Mar 29, 2013 at 10:09 AM, Scott Hernandez (JIRA)

Comment by Kevin J. Rice [ 26/Apr/13 ]

Specific usecase match here: inserting vals into an array. I have separate (indexed) field for max value in the array. thus doc:

{ maxval: 30, vals: [0, 10, 20, 30] }

Then, in two different processes, I $push a vals of 40 then 25. How do I update maxval to 40 then not update it to 25?

This hits the concept of mongo db.update() referring to a value that's in the record already. It's easy in SQL, not so much in Mongo.

Comment by Doug Paul [ 26/May/13 ]

Just upvoted this. We'd love to use it for dates, as well as for integers in an implementation of HyperLogLog.

In the meantime, my team is using a workaround: we replace the single field with an array, which contains (in the case of min) possible minima. When writing to the field, we add new values to the array using $addToSet. When reading, our software computes the minimum of all of the values in the array, and then removes all elements but the minimum using $pullAll.

For example, suppose we want to track the minimum and maximum heartrate from a series of measurements.

  1. Our document starts out as:

    { minHeartrate: [], maxHeartrate: [] }

  2. Then, we receive a measurement of 82, so we use add it to the arrays using $addToSet:

    { minHeartrate: [82], maxHeartrate: [82] }

  3. We then receive updates of 94, 89, which results in:

    { minHeartrate: [82, 94, 89], maxHeartrate: [82, 94, 89] }

  4. Now suppose a user of our application requests a view based on this document. Upon reading the document, the application computes the min and max as appropriate from the values in the array, and triggers a write to remove all other values. The minimum heartrate will of course be calculated as 82, and the maximum heartrate will be 94. So we will issue an update with the following modifier:

    { $pullAll: {minHeartrate: [94, 89], maxHeartrate: [82, 89]} }

    The document will then be:

    { minHeartrate: [82], maxHeartrate: [94] }

  5. If we were to read again at this point, no computation would be needed to find the minimum and maximum, because each array has exactly one value.

This approach has a couple of drawbacks, of course. First, the array could grow too large if too many distinct values are observed between reads. This can be mitigated by a helper thread in your application that finds all documents having min/max arrays with more than a single value and forcing a computation. Second, it offloads some of the work onto the read operations, which isn't very mongo-like. Third, I'm pretty sure it doesn't play well if the field(s) in question would be included in an index.

Comment by Scott Hernandez [ 27/Sep/13 ]

Doug, you will be able to do this with $push + $each + $sort + $slice in 2.6 (2.5.3 dev release will have it to test with) once we can sort on scalar values.

db.c.update({}, {$push: {maxHeartrate: {$each: [94,89], $sort: 1, $slice: -1 }}, minHeartrate: {$each: [94,89], $sort: -1, $slice: -1 }}})

Kevin, this will work for your example too, but will require you to store the max/min value as an array which is still effectively a single value, and it will work the same as you need.

Comment by auto [ 27/Sep/13 ]


{u'username': u'scotthernandez', u'name': u'Scott Hernandez', u'email': u'scotthernandez@gmail.com'}

Message: SERVER-1534 $min/$max update mods
Branch: master

Comment by auto [ 01/Oct/13 ]


{u'username': u'scotthernandez', u'name': u'Scott Hernandez', u'email': u'scotthernandez@gmail.com'}

Message: SERVER-1534 SERVER-10911: enable curentDate/min/max mods
Branch: master

Generated at Thu Dec 13 09:35:38 UTC 2018 using Jira 7.12.1#712002-sha1:609a50578ba6bc73dbf8b05dddd7c04a04b6807c.