MongoDB $function Operator

Intermediate
⏱️ 10 min read
📚 Updated: Jul 2026
🎯 4 Examples
Custom Logic

What You’ll Learn

The $function operator runs custom JavaScript inside MongoDB aggregation pipelines. When built-in operators are not enough, you can define your own logic with a body, pass values through args, and set lang to "js".

01

Custom JavaScript

Write your own logic.

02

Syntax

body, args, lang.

03

args Array

Pass field values in.

04

$addFields

Add computed results.

05

Use Cases

Grades, formatting, rules.

06

Prefer Native

Use built-ins when possible.

Definition and Usage

In MongoDB’s aggregation framework, the $function operator executes a JavaScript function for each document. You provide three parts: body (the function code), args (values passed as parameters), and lang (must be "js"). The return value of your function becomes the expression result — useful for custom grading rules, string formatting, or business logic that native operators cannot express cleanly.

💡
Beginner Tip

Your function cannot read this or access the document directly. Pass everything you need through args, like [ "$score", "$name" ]. Parameter order matches the args array order.

📝 Syntax

The $function operator takes an object with body, args, and lang:

mongosh
{
  $function: {
    body: <function or string>,
    args: [ <expression1>, ... ],
    lang: "js"
  }
}

Minimal Example

mongosh
{
  $function: {
    body: function(score) {
      return score >= 70 ? "pass" : "fail";
    },
    args: [ "$score" ],
    lang: "js"
  }
}

Syntax Rules

  • body — a JavaScript function or a string containing function code.
  • args — array of expressions (field paths like "$score" or literals) passed as function parameters.
  • lang — must be "js" (the only supported language).
  • Use inside stages like $project, $addFields, or $set.
  • The function must return a value compatible with BSON types.
⚠️
$function vs built-in operators

Custom JavaScript is slower and harder to maintain than native operators like $cond, $switch, or $map. Reach for $function only when built-ins cannot solve the problem. On self-hosted deployments, JavaScript execution may require additional server configuration.

⚡ Quick Reference

QuestionAnswer
Operator typeAggregation custom expression operator
Syntax{ $function: { body, args, lang: "js" } }
InputValues from args array
OutputFunction return value
Common stages$project, $addFields, $set
Pass/fail
{
  $function: {
    body: function(s) {
      return s >= 70 ? "pass" : "fail";
    },
    args: ["$score"],
    lang: "js"
  }
}

Grade from score

String body
{
  $function: {
    body: "function(a,b){return a+b;}",
    args: ["$x", "$y"],
    lang: "js"
  }
}

Body as string

Multiple args
{
  $function: {
    body: function(f,l){
      return f + " " + l;
    },
    args: ["$first","$last"],
    lang: "js"
  }
}

Combine fields

lang
lang: "js"

Required; only "js"

Examples Gallery

Walk through sample data, an aggregation pipeline, and the output step by step.

📚 Student Scores

Start with a students collection and compute pass/fail status with custom JavaScript.

Sample Input Documents

Suppose you have a students collection with name and score fields:

mongosh
[
  { "_id": ObjectId("609c26812e9274a86871bc6a"), "name": "Ana", "score": 85 },
  { "_id": ObjectId("609c26812e9274a86871bc6b"), "name": "Ben", "score": 62 },
  { "_id": ObjectId("609c26812e9274a86871bc6c"), "name": "Cara", "score": 74 }
]

Example 1 — Pass or Fail from Score

Use $addFields to add a result field with a custom function:

mongosh
db.students.aggregate([
  {
    $addFields: {
      result: {
        $function: {
          body: function(score) {
            return score >= 70 ? "pass" : "fail";
          },
          args: [ "$score" ],
          lang: "js"
        }
      }
    }
  }
])

How It Works

  • args: [ "$score" ] passes each document’s score as the first parameter.
  • The function returns "pass" when score ≥ 70, otherwise "fail".
  • The return value is stored in the new result field.
  • This same logic could use $cond, but $function scales better for complex rules.

📈 Practical Patterns

Pass multiple arguments, format strings, and use a string-based function body.

Example 2 — Build a Full Name from Two Fields

Pass firstName and lastName through args:

mongosh
db.users.aggregate([
  {
    $project: {
      email: 1,
      fullName: {
        $function: {
          body: function(first, last) {
            return (first || "") + " " + (last || "");
          },
          args: [ "$firstName", "$lastName" ],
          lang: "js"
        }
      }
    }
  }
])

How It Works

Parameter order matches args: first argument is firstName, second is lastName. Handle nulls inside your function when fields may be missing.

Example 3 — Custom Total with Array Logic

Loop over an array passed through args to compute a discounted total:

mongosh
db.orders.aggregate([
  {
    $addFields: {
      discountedTotal: {
        $function: {
          body: function(items, rate) {
            let total = 0;
            for (let i = 0; i < items.length; i++) {
              total += items[i].price * items[i].qty;
            }
            return total * (1 - rate);
          },
          args: [ "$items", "$discountRate" ],
          lang: "js"
        }
      }
    }
  }
])

How It Works

Arrays and numbers from the document are passed as regular JavaScript values. Your function can use loops, conditionals, and any JS logic MongoDB allows.

Example 4 — Function Body as a String

When storing pipelines in config files, a string body can be easier to serialize:

mongosh
db.products.aggregate([
  {
    $project: {
      name: 1,
      priceTier: {
        $function: {
          body: "function(price) { if (price < 50) return 'budget'; if (price < 200) return 'standard'; return 'premium'; }",
          args: [ "$price" ],
          lang: "js"
        }
      }
    }
  }
])

How It Works

The body string must contain a valid JavaScript function definition. MongoDB parses and executes it the same way as an inline function. Both forms are equivalent at runtime.

🚀 Use Cases

  • Custom business rules — grading, tiering, or eligibility logic that goes beyond $cond.
  • String formatting — build display labels from multiple fields with JavaScript string logic.
  • Array processing — loop over embedded arrays when $reduce or $map would be awkward.
  • Prototyping — test complex transformations before refactoring to native operators.

🧠 How $function Works

1

MongoDB evaluates args

Each expression in args is resolved — field paths like "$score" become actual values.

Input
2

JavaScript function runs

MongoDB calls your body function with those values as parameters, in order.

Execute
3

Return value is stored

Whatever your function returns becomes the expression result in $project or $addFields.

Output
=

Custom computed field

Each document gets a value produced by your JavaScript logic.

Conclusion

The $function operator unlocks custom JavaScript in MongoDB aggregation pipelines. It is powerful for business rules, formatting, and array logic that native operators cannot express cleanly.

For beginners, remember the three parts: body (your function), args (inputs from fields), and lang: "js". Start simple with one argument, then expand as your logic grows — but always check if $cond or $switch can do the job first.

💡 Best Practices

✅ Do

  • Pass all needed data through args
  • Prefer built-in operators when they are sufficient
  • Keep functions small and easy to test
  • Handle null or missing values inside your function
  • Use string body when storing pipelines in config

❌ Don’t

  • Use $function for simple logic that $cond handles
  • Assume the function can access the full document without args
  • Run heavy loops on large arrays in production without testing
  • Forget lang: "js" — it is required
  • Return values that are not valid BSON types

Key Takeaways

Knowledge Unlocked

Five things to remember about $function

Use these points when adding custom JavaScript to pipelines.

5
Core concepts
📝 02

Three Parts

body, args, lang.

Syntax
🛠 03

args Array

Pass field values in.

Usage
🔍 04

Complex Rules

Grades, tiers, totals.

Use case
05

Prefer Native

Use built-ins first.

Edge case

❓ Frequently Asked Questions

$function lets you run custom JavaScript inside an aggregation pipeline. You define a function body, pass arguments from document fields or expressions, and return a computed value for each document.
The syntax is { $function: { body: <function or string>, args: [ <expressions> ], lang: "js" } }. The body contains your JavaScript logic, args supplies inputs, and lang must be "js".
Use $function when built-in operators like $cond, $switch, or $map cannot express your logic cleanly. Prefer native operators for performance and portability whenever possible.
Not inside the function body. Pass field values through the args array, such as [ "$score", "$name" ]. The function receives those values as parameters in order.
Use $function inside expression stages such as $project, $addFields, and $set when you need custom per-document computation.

Explore More MongoDB Operators

Continue with $getField for dynamic field access, or review $cond for native conditionals.

Next: $getField →

About the author

Mari Selvan M P
Mari Selvan M P 🔗

Developer, cloud engineer, and technical writer

  • Experience 12 years building web and cloud systems
  • Focus Full Stack Development, AWS, and Developer Education

I write practical tutorials so students and working developers can learn by doing—from databases and APIs to deployment on AWS.

5 people found this page helpful