Variable and Reference Expressions

Variables and references — system variables, $expr for expressions in $match, and $let for local variables.

Previous | Index | Next: 15 - Set Operators


Reference expressions let you use variables, access system state, and bridge the gap between query syntax and expression syntax.


$$ Operator — System Variables

MongoDB defines several system variables accessible via the $$ prefix.

$$CURRENT

Refers to the current document being processed in a pipeline stage. When you write a field path like "$title", it is actually shorthand for "$$CURRENT.title".

// These two are equivalent:
"$title"
"$$CURRENT.title"

Example: Using $$CURRENT explicitly

db.projects.aggregate([
  {
    $match: {
      $expr: {
        $eq: ["$$CURRENT.type", "MANAGEMENT_PROJECT"]
      }
    }
  },
  {
    $sort: { "$$CURRENT.type": -1 }
  },
  { $out: "projectReport" }
]);

In practice, you’ll almost always use the shorter "$fieldName" form. But knowing about $$CURRENT helps when reading complex pipelines or when you need to be explicit about scope.


$expr — Use Expressions Inside $match

By default, the $match stage only accepts standard query operators ($eq, $gt, etc. in query syntax). It does not accept aggregation expressions.

The $expr operator bridges this gap — it lets you use any aggregation expression inside $match.

{
  $match: {
    $expr: { <expression> }
  }
}

Why is this needed?

Standard $match queries compare a field to a literal value:

{ $match: { price: { $gt: 100 } } }    // field vs literal — works fine

But what if you want to compare two fields or use a computed value? That requires expressions:

{ $match: { $expr: { $gt: ["$price", "$cost"] } } }    // field vs field

Example: Filter projects with funding below 5000

db.projects.aggregate([
  {
    $addFields: {
      projectFunding: { $sum: "$fundings.amount" }
    }
  },
  {
    $match: {
      $expr: {
        $lt: ["$projectFunding", 5000]
      }
    }
  },
  {
    $project: {
      projectFunding: 1,
      title: 1,
      description: 1
    }
  },
  {
    $sort: { projectFunding: 1 }
  },
  { $out: "projectReport" }
]);

When you need $expr vs. when you don’t

Standard $match (no $expr needed):
  { $match: { type: "REQUEST_PROJECT" } }
  { $match: { price: { $gt: 100 } } }
  
$expr required:
  { $match: { $expr: { $gt: ["$price", "$cost"] } } }        // field vs field
  { $match: { $expr: { $eq: ["$type", "$$expectedType"] } } } // using variables

$let — Define Local Variables

The $let operator creates scoped variables within an expression. This is useful for breaking complex calculations into readable steps.

{
  $let: {
    vars: {
      <var1>: <expression>,
      <var2>: <expression>,
      ...
    },
    in: <expression>    // can use $$var1, $$var2 here
  }
}

Variables defined in vars are only accessible within the in expression.

Example: Calculate adjusted funding

db.projects.aggregate([
  {
    $addFields: {
      projectFunding: {
        $let: {
          vars: {
            amount: { $sum: "$fundings.amount" }
          },
          in: {
            $multiply: ["$$amount", 1.2]    // 20% markup
          }
        }
      }
    }
  }
]);

This is equivalent to computing the sum first and then multiplying, but $let makes it clearer by naming the intermediate value.

When to use $let

  • When an expression is used multiple times — define it once as a variable
  • When a calculation has multiple steps — name each step for readability
  • When passing data into a $lookup subquery (via let in the $lookup stage — see 06 - Relationship Stages)

Variable Scope Summary

SyntaxScopeExample
$fieldNameCurrent document’s field"$title" → value of title
$$CURRENTCurrent document (explicit)"$$CURRENT.title" → same as "$title"
$$varNameVariable from $let or $lookup.let"$$amount"
$$thisCurrent element in $map/$reduceUsed inside in:
$$valueAccumulator in $reduceUsed inside in:

Next: 15 - Set Operators