MongoDB $min Aggregation Pipeline Operator

In MongoDB, the $min aggregation pipeline operator returns the minimum value from an expression.

Syntax

The $min operator supports two syntaxes.

Syntax 1:

{ $min: <expression> }

Syntax 2:

{ $min: [ <expression1>, <expression2> ... ]  }

The first syntax accepts one argument and the second syntax accepts multiple arguments.

When used in the $group stage, you can only use the first syntax. In this case, $min returns the minimum value that results from applying an expression to each document in a group of documents that share the same group by key.

Examples of Syntax 1 (Single Argument)

Here are a couple of examples that use the single argument syntax.

Grouped Documents

This example uses $min in conjunction with $group to return the minimum value from a group of documents that are grouped by key.

Suppose we have a collection called pets with the following documents:

{ "_id" : 1, "name" : "Wag", "type" : "Dog", "weight" : 20 }
{ "_id" : 2, "name" : "Bark", "type" : "Dog", "weight" : 10 }
{ "_id" : 3, "name" : "Meow", "type" : "Cat", "weight" : 7 }
{ "_id" : 4, "name" : "Scratch", "type" : "Cat", "weight" : 8 }
{ "_id" : 5, "name" : "Bruce", "type" : "Kangaroo", "weight" : 100 }
{ "_id" : 6, "name" : "Hop", "type" : "Kangaroo", "weight" : 130 }
{ "_id" : 7, "name" : "Punch", "type" : "Kangaroo", "weight" : 200 }
{ "_id" : 8, "name" : "Snap", "type" : "Cat", "weight" : 12 }
{ "_id" : 9, "name" : "Ruff", "type" : "Dog", "weight" : 30 }

We can group these documents by their type field, and then use $min to return the minimum value of the weight field for each group:

db.pets.aggregate(
   [
     {
       $group:
          {
            _id: "$type",
            min: { $min: "$weight" }
          }
     }
   ]
)

Result:

{ "_id" : "Kangaroo", "min" : 100 }
{ "_id" : "Dog", "min" : 10 }
{ "_id" : "Cat", "min" : 7 }

Arrays

This example applies $min to a single document that contains a field with an array of values.

This option is only available when using the single argument syntax. Arrays are ignored when using the multi-argument syntax (more on this below).

Suppose we have a collection called players with the following documents:

{ "_id" : 1, "player" : "Homer", "scores" : [ 1, 7, 2, 3, 8, 7, 1 ] }
{ "_id" : 2, "player" : "Marge", "scores" : [ 0, 1, 8, 17, 18, 8 ] }
{ "_id" : 3, "player" : "Bart", "scores" : [ 15, 11, 8, 0, 1, 3 ] }
{ "_id" : 4, "player" : "Brian", "scores" : [ 7 ] }
{ "_id" : 5, "player" : "Farnsworth", "scores" : [ ] }
{ "_id" : 6, "player" : "Meg", "scores" : null }
{ "_id" : 7, "player" : "Ron" }

We can apply $min to the scores field in each document:

db.players.aggregate(
   [
     {
       $project:
          {
            player: 1,
            min: { $min: "$scores" }
          }
     }
   ]
)

Result:

{ "_id" : 1, "player" : "Homer", "min" : 1 }
{ "_id" : 2, "player" : "Marge", "min" : 0 }
{ "_id" : 3, "player" : "Bart", "min" : 0 }
{ "_id" : 4, "player" : "Brian", "min" : 7 }
{ "_id" : 5, "player" : "Farnsworth", "min" : null }
{ "_id" : 6, "player" : "Meg", "min" : null }
{ "_id" : 7, "player" : "Ron", "min" : null }

In this case, the first four documents returned the minimum value from the various numbers that were in their respective arrays.

In document 4’s case, this was the same as the number, because there was only one number in the array.

Document 5 returned null because we provided an empty array.

Document 6 returned null because we provided null as the argument.

Document 7 returned null because the field didn’t even exist.

Example of Syntax 2 (Multiple Arguments)

The second syntax involves providing $min with more than one argument. $min then returns the minimum value from all of the supplied arguments.

Suppose we have a collection called data with the following document:

{ "_id" : 1, "a" : 10, "b" : 500, "c" : -900, "d" : 4 }

We can use $min to return the minimum value from the a, b, c, and d fields:

db.data.aggregate(
   [
     { $match: { _id: { $in: [ 1 ] } } },
     {
       $project:
          {
            min: { $min: [ "$a", "$b", "$c", "$d" ] }
          }
     }
   ]
)

Result:

{ "_id" : 1, "min" : -900 }

In this case, -900 was the minimum value.

Missing Fields

When using the multi-argument syntax, $min ignores any missing fields. That is, if you supply a field that doesn’t exist, it ignores it. If none of the fields exist, then it returns null.

Example:

db.data.aggregate(
   [
     { $match: { _id: { $in: [ 1 ] } } },
     {
       $project:
          {
            min: { $min: [ "$a", "$b", "$c", "$d", "$e" ] }
          }
     }
   ]
)

Result:

{ "_id" : 1, "min" : -900 }

In this case I provided an extra field ($e) that doesn’t exist in the document. $min calculated the minimum value based on the remaining fields that do exist.

However, here’s what happens when none of the fields exist:

db.data.aggregate(
   [
     { $match: { _id: { $in: [ 1 ] } } },
     {
       $project:
          {
            result: { $min: [ "$x", "$y", "$z" ] }
          }
     }
   ]
)

Result:

{ "_id" : 1, "result" : null }

The result is null.

As we saw earlier, when using the single-argument syntax, a missing field results in null.

Example:

db.pets.aggregate(
   [
     {
       $group:
          {
            _id: "$type",
            min: { $min: "$oops!" }
          }
     }
   ]
)

Result:

{ "_id" : "Dog", "min" : null }
{ "_id" : "Cat", "min" : null }
{ "_id" : "Kangaroo", "min" : null }

Comparing Different Types

The $min operator compares both value and type. When the values are of different types, $min calculates the minimum value based on the BSON comparison order.

Suppose our collection contains the following documents:

{ "_id" : 2, "a" : 1, "b" : 2, "c" : 3, "d" : [ 0 ] }
{ "_id" : 3, "a" : 1, "b" : 2, "c" : 3, "d" : "0" }
{ "_id" : 4, "a" : "One", "b" : "Two", "c" : "Three", "d" : "Four" }
{
	"_id" : 5,
	"a" : ISODate("1999-01-03T23:30:15.100Z"),
	"b" : ISODate("2000-01-03T23:30:15.100Z")
}
{
	"_id" : 6,
	"a" : ISODate("1999-01-03T23:30:15.100Z"),
	"b" : "2000-01-03T23:30:15.100Z"
}

With the exception of document 4, each of those documents use mixed types (there’s at least one type that’s different to the others across data fields). Document 4 uses strings in all four fields.

Here’s what happens when we apply $min to those documents:

db.data.aggregate(
   [
     { $match: { _id: { $in: [ 2, 3, 4, 5, 6 ] } } },
     {
       $project:
          {
            min: { $min: [ "$a", "$b", "$c", "$d" ] }
          }
     }
   ]
)

Result:

{ "_id" : 2, "min" : 1 }
{ "_id" : 3, "min" : 1 }
{ "_id" : 4, "min" : "Four" }
{ "_id" : 5, "min" : ISODate("1999-01-03T23:30:15.100Z") }
{ "_id" : 6, "min" : "2000-01-03T23:30:15.100Z" }

Regarding the document with an _id of 2, numbers are less than arrays, so the number 1 is returned (even though the array contains a number that’s less than all the other numbers).

Document 3: Numbers are less than strings, and so the lowest number is returned.

Document 4: All fields are strings, and so Four is the minimum string.

Document 5: Two dates are supplied, and so the earlier date is returned.

Document 6: In this case, a Date object and a date string are supplied. Strings are less than Date objects, and so the string is returned (even though its date is later than the Date object’s).

Available Stages

$min is available in the in the following stages:

  • $group
  • $project
  • $addFields
  • $set
  • $replaceRoot
  • $replaceWith
  • $match stage that includes an $expr expression