In MongoDB, the $max
aggregation pipeline operator returns the maximum value from an expression.
Syntax
The $max
operator supports two syntaxes.
Syntax 1:
{ $max: <expression> }
Syntax 2:
{ $max: [ <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, $max
returns the maximum 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 $max
in conjunction with $group
to return the maximum 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 $max
to return the maximum value of the weight
field for each group:
db.pets.aggregate(
[
{
$group:
{
_id: "$type",
max: { $max: "$weight" }
}
}
]
)
Result:
{ "_id" : "Kangaroo", "max" : 200 } { "_id" : "Cat", "max" : 12 } { "_id" : "Dog", "max" : 30 }
Arrays
This example applies $max
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 $max
to the scores
field in each document:
db.players.aggregate(
[
{
$project:
{
player: 1,
max: { $max: "$scores" }
}
}
]
)
Result:
{ "_id" : 1, "player" : "Homer", "max" : 8 } { "_id" : 2, "player" : "Marge", "max" : 18 } { "_id" : 3, "player" : "Bart", "max" : 15 } { "_id" : 4, "player" : "Brian", "max" : 7 } { "_id" : 5, "player" : "Farnsworth", "max" : null } { "_id" : 6, "player" : "Meg", "max" : null } { "_id" : 7, "player" : "Ron", "max" : null }
In this case, the first four documents returned the maximum 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 $max
with more than one argument. $max
then returns the maximum 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 $max
to return the maximum value from the a
, b
, c
, and d
fields:
db.data.aggregate(
[
{ $match: { _id: { $in: [ 1 ] } } },
{
$project:
{
max: { $max: [ "$a", "$b", "$c", "$d" ] }
}
}
]
)
Result:
{ "_id" : 1, "max" : 500 }
In this case, 500
was the maximum value.
Missing Fields
When using the multi-argument syntax, $max
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:
{
max: { $max: [ "$a", "$b", "$c", "$d", "$e" ] }
}
}
]
)
Result:
{ "_id" : 1, "max" : 500 }
In this case I provided an extra field ($e
) that doesn’t exist in the document. $max
calculated the maximum 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: { $max: [ "$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",
max: { $max: "$oops!" }
}
}
]
)
Result:
{ "_id" : "Dog", "max" : null } { "_id" : "Kangaroo", "max" : null } { "_id" : "Cat", "max" : null }
Comparing Different Types
The $max
operator compares both value and type. When the values are of different types, $max
calculates the maximum value based on the BSON comparison order.
Suppose our collection contains the following documents:
{ "_id" : 2, "a" : 1, "b" : 2, "c" : 3, "d" : [ 1 ] } { "_id" : 3, "a" : 1, "b" : 2, "c" : 3, "d" : "1" } { "_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 $max
to those documents:
db.data.aggregate(
[
{ $match: { _id: { $in: [ 2, 3, 4, 5, 6 ] } } },
{
$project:
{
max: { $max: [ "$a", "$b", "$c", "$d" ] }
}
}
]
)
Result:
{ "_id" : 2, "max" : [ 1 ] } { "_id" : 3, "max" : "1" } { "_id" : 4, "max" : "Two" } { "_id" : 5, "max" : ISODate("2000-01-03T23:30:15.100Z") } { "_id" : 6, "max" : ISODate("1999-01-03T23:30:15.100Z") }
the document with an _id
of 2
, arrays are greater than numbers, so the array is returned (even though its element is a number that’s less than some of the other numbers).
Document 3: Strings are greater than numbers, and so the string is returned.
Document 4: All fields are strings, and so Two
is the greatest string.
Document 5: Two dates are supplied, and so the later date is returned.
Document 6: In this case, a Date object and a date string are supplied. Date objects are greater than strings, and so the Date object is returned (even though its date is earlier than the string’s).
Available Stages
$max
is available in the in the following stages:
$group
$project
$addFields
$set
$replaceRoot
$replaceWith
$match
stage that includes an$expr
expression