$function is a MongoDB aggregation expression that runs custom JavaScript for each document. You pass inputs through args, write logic in body, and set lang to "js". Prefer built-in operators when they can do the same job.
Remember
$function = custom JS inside aggregation
{
$function: {
body: function(score) { ... },
args: [ "$score" ],
lang: "js"
}
}
args → function parameters (in order)
body → return a value
lang → must be "js"
prefer $cond / $switch when possible
Write body as a function whose parameters match that order.
Set lang: "js" and return the value you want stored.
Piece
Required?
Role
body
Yes
JavaScript function (or string form)
args
Yes
Values passed into the function
lang
Yes
Must be "js"
Pseudocode
Pseudocode
for each document:
values = evaluate each expression in args
result = run JavaScript body(values...)
emit { ..., field: result }
Reference
Cheat Sheet
Keep these facts handy while writing custom pipeline functions.
Topic
Detail
Type
Aggregation expression (not a find filter)
Syntax
{ $function: { body, args, lang: "js" } }
Field access
Only via args, not this
Performance
Slower than native operators
First choice
$cond, $switch, math ops
Sample check
score 85 → grade "B"
Try it
Live Preview
Watch a small grading function turn a score into a letter—the same idea as $function below.
Simulates body: function(score) { ... } with args: [score].
Result
score = 85
$function → "B"
{ grade: "B" }
Trace
Worked Walkthrough — Letter Grade
Asha scored 85. We want a letter grade with a tiny JavaScript function.
Calculation
Document:
{ name: "Asha", score: 85 }
args: [ 85 ]
body(score):
if score >= 90 → "A"
if score >= 80 → "B"
if score >= 70 → "C"
else → "F"
85 >= 80 → "B" ✓
Code
MongoDB Examples
We use one student document. First compute a letter grade from score, then build a short message from name and score. Run these in mongosh after inserting the sample data once.
Example 1 — Sample Data
Imagine one student named Asha with score 85. We save that in a collection named students.
Same stored document. Still using Asha and score 85 from Example 1.
Args map by order. First arg → name, second → score.
Return a string. The function concatenates them into "Asha scored 85".
Edge Cases & Pitfalls
Check these before using $function in production pipelines.
args
No direct field access in body
You cannot read $score inside the function. Pass it through args first.
perf
Prefer built-in operators
Native ops like $cond and $switch are faster and easier to maintain for simple logic.
server
Needs server-side JavaScript
Some deployments disable JS. If $function errors, check server settings and Atlas feature support for your tier.
size
Keep the body tiny
Heavy loops or large scripts hurt latency. Move complex work to the application layer when possible.
Analysis
Performance Notes
Approach
Cost
Best for
Built-in operators
Lowest
Most day-to-day pipeline math/logic
Small $function
Higher per doc
Logic that is awkward in expression trees
Large JS body
Highest
Avoid on large collections
Filter early with $match so JavaScript runs on fewer documents.
Remember
Key Takeaways
Role: custom JavaScript inside aggregation.
Required:body, args, lang: "js".
Inputs: only through args (by parameter order).
Default choice: use built-ins first; $function as escape hatch.
One line:$function runs small server-side JS in the pipeline—pass values via args, return a result.
Frequently Asked Questions
$function runs custom JavaScript inside an aggregation pipeline. You supply a function body, an args array of inputs, and lang: "js". The return value becomes the expression result for each document.
Use { $function: { body: <function or string>, args: [ <expressions> ], lang: "js" } }. body holds the JavaScript, args passes values into the function parameters in order, and lang must be "js".
No. Pass field values through args, such as [ "$score", "$name" ]. Inside the function those values arrive as parameters.
Use it when built-in operators cannot express the logic cleanly. Prefer native operators like $cond or $switch for speed and simpler maintenance whenever possible.
It is usually slower than equivalent built-in expressions because the server runs JavaScript per document. Keep bodies small and avoid it on hot paths when a native operator works.
Inside expression stages such as $project, $addFields, and $set for per-document custom computation.