Details

    • Type: New Feature
    • Status: Closed
    • Priority: Minor - P4
    • Resolution: Fixed
    • Affects Version/s: None
    • Fix Version/s: 2.5.3
    • Component/s: Write Ops
    • Labels:
      None
    • Backwards Compatibility:
      Fully Compatible

      Description

      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.

      Examples:

      > db.v.find()
      {_id:1, v:1}
      {_id:2, v:2}
      {_id:3}
      {_id:4}
      > 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.

        Issue Links

          Activity

          Hide
          justanyone Kevin J. Rice added a comment -

          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.

          Show
          justanyone Kevin J. Rice added a comment - 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.
          Hide
          dpaul@proteuseng.com Doug Paul added a comment - - edited

          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.

          Show
          dpaul@proteuseng.com Doug Paul added a comment - - edited 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. Our document starts out as: { minHeartrate: [], maxHeartrate: [] } Then, we receive a measurement of 82, so we use add it to the arrays using $addToSet : { minHeartrate: [82], maxHeartrate: [82] } We then receive updates of 94, 89, which results in: { minHeartrate: [82, 94, 89], maxHeartrate: [82, 94, 89] } 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] } 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.
          Hide
          scotthernandez Scott Hernandez (Inactive) added a comment -

          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.

          Show
          scotthernandez Scott Hernandez (Inactive) added a comment - 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.
          Hide
          auto auto (Inactive) added a comment -

          Author:

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

          Message: SERVER-1534 $min/$max update mods
          Branch: master
          https://github.com/mongodb/mongo/commit/f294acf6512a11ee06ed88555b02947f2fbe2c79

          Show
          auto auto (Inactive) added a comment - Author: {u'username': u'scotthernandez', u'name': u'Scott Hernandez', u'email': u'scotthernandez@gmail.com'} Message: SERVER-1534 $min/$max update mods Branch: master https://github.com/mongodb/mongo/commit/f294acf6512a11ee06ed88555b02947f2fbe2c79
          Hide
          auto auto (Inactive) added a comment -

          Author:

          {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
          https://github.com/mongodb/mongo/commit/278f1b43f51a62989ac91741b20fbf3adb6733b2

          Show
          auto auto (Inactive) added a comment - Author: {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 https://github.com/mongodb/mongo/commit/278f1b43f51a62989ac91741b20fbf3adb6733b2

            People

            • Votes:
              38 Vote for this issue
              Watchers:
              33 Start watching this issue

              Dates

              • Created:
                Updated:
                Resolved: