MongoDB $substrBytes Operator

Beginner
5 min read
Updated: Oct 2026
3 examples
Live preview

What Is $substrBytes?

$substrBytes is a MongoDB aggregation expression that returns a substring by UTF-8 byte index and byte count. For ASCII text, bytes match characters. For accents and emoji, one character can use 2–4 bytes—slices must land on character boundaries or the pipeline errors.

Remember
$substrBytes = substring by UTF-8 bytes

{ $substrBytes: [ <string>, <byteIndex>, <byteCount> ] }

"13Q1" + 0, 2 → "13"
"abcde" + 1, 2 → "bc"
"café…" + mid-character → pipeline error

null / missing string → ""
need characters? → use $substrCP
legacy name → $substr (alias)
UsePrefer
Byte / storage-oriented slice$substrBytes
Character-safe Unicode slice$substrCP
Measure bytes first$strLenBytes
Legacy name$substr (alias of this operator)

How to Use $substrBytes in MongoDB

Pass the string, a non-negative byte start, and a non-negative byte count on valid boundaries.

  1. Choose an expression stage ($addFields or $project).
  2. Pass the string expression as the first argument.
  3. Set byte index and byte count (both ≥ 0), aligned to UTF-8 character edges.
  4. For “rest of string,” compute remaining bytes with $strLenBytes and $subtract.
PieceRequired?Role
StringYesSource text (field or literal)
Byte indexYesZero-based UTF-8 start (≥ 0)
Byte countYesHow many UTF-8 bytes to take (≥ 0)

Pseudocode

Pseudocode
for each document:
    s = evaluate(string)
    i = evaluate(byteIndex)
    n = evaluate(byteCount)
    if s is null or missing:
        result = ""
    else if i or n is negative, or cut is mid-character:
        error
    else:
        result = utf8Bytes(s) from i for n bytes
    emit { ..., field: result }

Cheat Sheet

Keep these facts handy while slicing by bytes.

TopicDetail
TypeAggregation expression (not a find filter)
Syntax{ $substrBytes: [ <string>, <index>, <count> ] }
UnitsUTF-8 bytes (1–4 per character)
Null / missingReturns ""
Rest of string$substrBytes([s, i, { $subtract: [ { $strLenBytes: s }, i ] } ])
vs $substrCPBytes vs Unicode code points
Sample check$substrBytes(["13Q1", 0, 2]) → "13"

Live Preview

Watch $substrBytes cut ASCII text by byte offsets. (For multi-byte text, stay on character boundaries.)

Simulates $substrBytes on ASCII (same idea as the mongosh examples below).

Result
string = "13Q1"
index  = 0
count  = 2

$substrBytes → "13"

{ year: "13" }

Worked Walkthrough — 13Q1

A quarter code is pure ASCII, so each character is 1 byte. We pull "13" then the remaining "Q1".

Calculation
String: "13Q1"
Bytes:   0 1 2 3
Values:  1 3 Q 1

Year (2 bytes from 0):
  $substrBytes(["$quarter", 0, 2]) → "13"

Rest (length − 2 bytes from 2):
  len = $strLenBytes("$quarter") → 4
  count = 4 − 2 → 2
  $substrBytes(["$quarter", 2, 2]) → "Q1"

Check: "13" + "Q1" = "13Q1" ✓

MongoDB Examples

We store one inventory item with quarter code 13Q1. First we extract the year by bytes, then the rest using $strLenBytes. Run these in mongosh after inserting the sample data once.

Example 1 — Sample Data

Imagine one product with item code ABC1 and quarter 13Q1. We save it in a collection named inventory.

mongosh
db.inventory.insertOne({
  _id: 1,
  item: "ABC1",
  quarter: "13Q1"
});

How the Data Is Stored in MongoDB

After the insert, MongoDB keeps one document in inventory. Run find() to see it:

mongosh
db.inventory.find()

Read that document like this:

Field in MongoDBValuePlain English
item"ABC1"Product code
quarter"13Q1"4 ASCII bytes: year 13 + label Q1
Picture
quarter bytes: 1 3 Q 1
index:         0 1 2 3

bytes 0–1 → "13"
bytes 2–3 → "Q1"

How It Works

  1. Insert once. insertOne adds a single inventory document.
  2. Quarter is ASCII. Each letter/digit is exactly one UTF-8 byte.
  3. find() shows the same shape. What you inserted is what you get back—ready for $substrBytes next.

Example 2 — Extract the Year (2 Bytes)

Goal: take the first two UTF-8 bytes of quarter as the year ("13").

mongosh
db.inventory.aggregate([
  {
    $addFields: {
      year: {
        $substrBytes: ["$quarter", 0, 2]
      }
    }
  }
]);

How It Works

  1. Read the stored string. MongoDB already has quarter: "13Q1" from Example 1.
  2. Start at byte 0. Extraction begins at the first UTF-8 byte.
  3. Take 2 bytes. That yields "13".
  4. Keep all fields. $addFields adds year next to the original quarter.

Example 3 — Extract the Rest of the String

Same document: start at byte 2 and take the remaining bytes using $strLenBytes minus 2.

mongosh
db.inventory.aggregate([
  {
    $addFields: {
      year: { $substrBytes: ["$quarter", 0, 2] },
      qtr: {
        $substrBytes: [
          "$quarter",
          2,
          { $subtract: [{ $strLenBytes: "$quarter" }, 2] }
        ]
      }
    }
  }
]);

How It Works

  1. Same stored document. Still using "13Q1" from Example 1.
  2. Measure total bytes. $strLenBytes returns 4.
  3. Remaining count. $subtract does 4 − 2 = 2.
  4. Slice from byte 2. Two bytes yields "Q1".

Edge Cases & Pitfalls

Check these before using $substrBytes in production pipelines.

UTF-8

Mid-character slices error

Start or end inside a multi-byte character fails the pipeline. Prefer $substrCP for character thinking.

count

No negative length shortcut

Unlike legacy $substr with -1, byte count must be ≥ 0. Use $strLenBytes − index for the remainder.

null

Null becomes empty string

Null or missing string input returns "", not an error. Non-string values still fail.

ASCII

ASCII looks like characters

For A–Z codes and digits, bytes equal characters. The difference appears with accents and emoji.

Performance Notes

ApproachCostBest for
$substrBytes alonePer-document byte sliceFixed ASCII prefixes
+ $strLenBytesLength + sliceDynamic “rest of string”
Store split fieldsNo runtime sliceHot filters on year / prefix

Byte slicing is cheap for short strings. Filter early with $match when only some documents need a prefix extracted.

Key Takeaways

  • Role: substring by UTF-8 byte index + count.
  • Boundaries: never cut mid-character.
  • Rest: use $strLenBytes − start (not length -1).
  • Characters: use $substrCP instead.

One line: $substrBytes answers “which UTF-8 bytes do I get from this start for this count?”—on valid character boundaries only.

Frequently Asked Questions

$substrBytes returns part of a string starting at a zero-based UTF-8 byte index for a given byte count. For ASCII text, byte positions match character positions. Use it inside aggregation expression stages such as $project and $addFields.
Use { $substrBytes: [ <string>, <byte index>, <byte count> ] }. Both index and count must resolve to non-negative integers. Slices must land on valid UTF-8 character boundaries.
$substrBytes uses byte offsets (storage-oriented). $substrCP uses Unicode code-point offsets (character-oriented). For accents and emoji, prefer $substrCP for user-facing slices.
MongoDB errors. The start index or end of the range cannot land in the middle of a UTF-8 character. Use $substrCP when you think in characters, not bytes.
If the string is null or the field is missing, $substrBytes returns an empty string "". Non-string values cause an error.
Unlike legacy $substr with length -1, $substrBytes needs a non-negative count. Compute remaining bytes with $subtract and $strLenBytes, or use $substrCP if you want character-based slicing.

Next: $substrCP

Slice strings by Unicode code points when characters matter more than bytes.

$substrCP tutorial →

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.

9 people found this page helpful