$expr
On this page
Definition
Changed in version 5.0.
$expr
Allows the use of expressions within a query predicate.
Compatibility
You can use $expr
for deployments hosted in the following
environments:
MongoDB Atlas: The fully managed service for MongoDB deployments in the cloud
MongoDB Enterprise: The subscription-based, self-managed version of MongoDB
MongoDB Community: The source-available, free-to-use, and self-managed version of MongoDB
Syntax
{ $expr: { <expression> } }
The argument can be any valid expression.
Behavior
$expr in $lookup Operations
When $expr
appears in a $match
stage that is part of a
$lookup
subpipeline, $expr
can refer to let
variables defined by the $lookup
stage. For an example, see
Use Multiple Join Conditions and a Correlated Subquery.
The $eq
, $lt
, $lte
,
$gt
, and $gte
comparison operators placed in
an $expr
operator can use an index on the from
collection
referenced in a $lookup
stage. Limitations:
Indexes can only be used for comparisons between fields and constants, so the
let
operand must resolve to a constant.For example, a comparison between
$a
and a constant value can use an index, but a comparison between$a
and$b
cannot.Indexes are not used for comparisons where the
let
operand resolves to an empty or missing value.Multikey indexes are not used.
Examples
Compare Two Fields from a Single Document
$expr
can contain expressions that compare fields from the same
document.
Create a monthlyBudget
collection with these documents:
db.monthlyBudget.insertMany( [ { _id : 1, category : "food", budget : 400, spent : 450 }, { _id : 2, category : "drinks", budget : 100, spent : 150 }, { _id : 3, category : "clothes", budget : 100, spent : 50 }, { _id : 4, category : "misc", budget : 500, spent : 300 }, { _id : 5, category : "travel", budget : 200, spent : 650 } ] )
The following operation uses $expr
to find documents
where the spent
amount exceeds the budget
:
db.monthlyBudget.find( { $expr: { $gt: [ "$spent" , "$budget" ] } } )
Output:
{ _id : 1, category : "food", budget : 400, spent : 450 } { _id : 2, category : "drinks", budget : 100, spent : 150 } { _id : 5, category : "travel", budget : 200, spent : 650 }
Use $expr With Conditional Statements
Some queries require the ability to execute conditional logic when
defining a query filter. The aggregation pipeline provides the
$cond
operator to express conditional statements. By using
$expr
with the $cond
operator, you can
specify a conditional filter for your query statement.
Create a sample supplies
collection with the following documents:
db.supplies.insertMany( [ { _id : 1, item : "binder", qty : NumberInt("100"), price : NumberDecimal("12") }, { _id : 2, item : "notebook", qty : NumberInt("200"), price : NumberDecimal("8") }, { _id : 3, item : "pencil", qty : NumberInt("50"), price : NumberDecimal("6") }, { _id : 4, item : "eraser", qty : NumberInt("150"), price : NumberDecimal("3") }, { _id : 5, item : "legal pad", qty : NumberInt("42"), price : NumberDecimal("10") } ] )
Assume that for an upcoming sale next month, you want to discount the prices such that:
If
qty
is greater than or equal to 100, the discounted price will be 0.5 of theprice
.If
qty
is less than 100, the discounted price is 0.75 of theprice
.
Before applying the discounts, you would like to know which items in the
supplies
collection have a discounted price of less than 5
.
The following example uses $expr
with $cond
to
calculate the discounted price based on the qty
and
$lt
to return documents whose calculated discount price
is less than NumberDecimal("5")
:
// Aggregation expression to calculate discounted price let discountedPrice = { $cond: { if: { $gte: ["$qty", 100] }, then: { $multiply: ["$price", NumberDecimal("0.50")] }, else: { $multiply: ["$price", NumberDecimal("0.75")] } } }; // Query the supplies collection using the aggregation expression db.supplies.find( { $expr: { $lt:[ discountedPrice, NumberDecimal("5") ] } });
The following table shows the discounted price for each document and
whether discounted price is less than NumberDecimal("5")
(i.e.
whether the document meets the query condition).
Document | Discounted Price | < NumberDecimal("5") |
---|---|---|
{"_id": 1, "item": "binder", "qty": 100, "price": NumberDecimal("12") } | NumberDecimal("6.00") | false |
{"_id": 2, "item": "notebook", "qty": 200, "price": NumberDecimal("8") } | NumberDecimal("4.00") | true |
{"_id": 3, "item": "pencil", "qty": 50, "price": NumberDecimal("6") } | NumberDecimal("4.50") | true |
{"_id": 4, "item": "eraser", "qty": 150, "price": NumberDecimal("3") } | NumberDecimal("1.50") | true |
{"_id": 5, "item": "legal pad", "qty": 42, "price": NumberDecimal("10") } | NumberDecimal("7.50") | false |
The db.collection.find()
operation returns the documents whose
calculated discount price is less than NumberDecimal("5")
:
{ _id : 2, item : "notebook", qty : 200 , price : NumberDecimal("8") } { _id : 3, item : "pencil", qty : 50 , price : NumberDecimal("6") } { _id : 4, item : "eraser", qty : 150 , price : NumberDecimal("3") }
Even though $cond
calculates an effective discounted
price, that price is not reflected in the returned documents. Instead,
the returned documents represent the matching documents in their
original state. The find operation did not return the binder
or legal pad
documents, as their discounted price was greater than
5
.