MongoDB $indexOfBytes Operator

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

What Is $indexOfBytes?

$indexOfBytes is a MongoDB aggregation expression that returns the zero-based UTF-8 byte index of the first match of a substring inside a string. Not found → -1. For plain English (ASCII) text, byte index equals character index.

Remember
$indexOfBytes ≈ string indexOf, counted in UTF-8 bytes

{ $indexOfBytes: [ <string>, <substring>, <start>, <end> ] }

found     → first byte index (0, 1, 2, …)
not found → -1
null / missing string → null
non-string (not null) → pipeline error
UsePrefer
Byte position of a substring$indexOfBytes
Character (code point) position$indexOfCP
Position of a value in an array$indexOfArray

How to Use $indexOfBytes in MongoDB

Pass the string, the substring to find, and optional byte range bounds.

  1. Choose an expression stage ($addFields or $project).
  2. Pass [ "$field", "needle" ] to get the first byte index.
  3. Optionally add start (and end) as non-negative byte offsets.
  4. Treat -1 as “not found”; treat null as “no string”.
PieceRequired?Role
StringYesExpression that resolves to a string
SubstringYesText to locate (must be a string)
Start / endNoSearch from byte start up to (not including) end

Pseudocode

Pseudocode
for each document:
    s = evaluate(stringExpression)
    needle = evaluate(substringExpression)
    if s is null or missing:
        result = null
    else if s is not a string:
        error
    else if needle is null or not a string:
        error
    else:
        from = start or 0          // byte offset
        to   = end or byteLength(s)
        result = first UTF-8 byte index of needle in s[from..to)
        if not found: result = -1
    emit { ..., field: result }

Cheat Sheet

Keep these facts handy while writing string-search expressions.

TopicDetail
TypeAggregation expression (not a find filter)
Syntax{ $indexOfBytes: [ string, substring, start?, end? ] }
UnitUTF-8 bytes (not characters)
FoundFirst matching byte index
Not found-1
Null / missing stringnull
ASCII tipFor A–Z / digits / spaces, byte index = character index

Live Preview

String: "foo.bar.baz" (ASCII — bytes match characters). Pick a substring and optional start byte.

Simulates $indexOfBytes with an optional start byte (same idea as the mongosh examples).

Result
text = "foo.bar.baz"
search = ".", start = 0
$indexOfBytes → 3

{ index: 3 }

Worked Walkthrough — Find a Dot

Text "foo.bar.baz". First "." is at byte 3; searching from byte 4 finds the second "." at 7.

Calculation
text = "foo.bar.baz"
bytes:  0123456789A   (each letter = 1 byte in ASCII)

$indexOfBytes(text, ".")
  → first "." at byte 3 → 3

$indexOfBytes(text, ".", 4)
  → skip "foo." → next "." at byte 7 → 7

$indexOfBytes(text, "zzz")
  → no hit → -1

Tip: for accents/emoji, use $indexOfCP if you want character positions.

MongoDB Examples

We use a small messages collection with a text field. First we find a substring’s byte index, then we search from a start byte. Insert the sample data once in mongosh.

Example 1 — Sample Data

Imagine three short messages. Each has an id and a text string. We save them in a collection named messages.

mongosh
db.messages.insertMany([
  { _id: 1, text: "hello world" },
  { _id: 2, text: "foo.bar.baz" },
  { _id: 3, text: "code to fun" }
]);

How the Data Is Stored in MongoDB

After the insert, MongoDB keeps three documents in messages. Run find() to see them:

mongosh
db.messages.find()

Read those documents like this:

Field in MongoDBExamplePlain English
_id1Message id for this lesson
text"hello world"String we will search
Picture
"hello world" (ASCII = 1 byte per character)

  index: 0123456789A
  chars: h e l l o   w o r l d
         0 1 2 3 4 5 6 7 8 9 10

  "world" starts at byte 6
  "xyz"   → not here (-1)

How It Works

  1. Insert once. insertMany adds three message documents.
  2. MongoDB stores strings as written. Spaces and dots count as characters (and bytes in ASCII).
  3. find() shows the same shape. What you inserted is what you get back—ready for $indexOfBytes next.

Example 2 — Find First Index

Goal: for each message, find where "o" first appears (byte index in ASCII text).

mongosh
db.messages.aggregate([
  {
    $addFields: {
      o_at: {
        $indexOfBytes: [ "$text", "o" ]
      }
    }
  }
]);

How It Works

  1. Read the stored string. "$text" is the message for each document.
  2. Search for “o”. $indexOfBytes walks from byte 0.
  3. First hit wins. hello → 4 (the “o” in hello); foo → 1; code → 1.
  4. ASCII shortcut. Each letter is one byte, so these indexes match character positions too.

Example 3 — Search From a Start Byte

Same messages, but find "." starting at byte 4. That skips the first segment of "foo.bar.baz".

mongosh
db.messages.aggregate([
  {
    $addFields: {
      dot_from_4: {
        $indexOfBytes: [ "$text", ".", 4 ]
      }
    }
  }
]);

How It Works

  1. Same stored documents. Still using the three messages from Example 1.
  2. Start at byte 4. MongoDB ignores bytes 0–3 for this search.
  3. foo.bar.baz. First "." is at 3 (skipped); next "." is at 7.
  4. Others stay -1. hello and code have no "." after the start byte.

Edge Cases & Pitfalls

Check these before using $indexOfBytes in production pipelines.

utf-8

Bytes ≠ characters

Accented letters and emoji often use more than one UTF-8 byte. Prefer $indexOfCP when you want character positions.

-1

-1 means not found

Do not treat -1 as a valid cut point for $substrBytes. Branch with $cond first.

null

Null string vs null substring

A null/missing string returns null. A null substring errors. Keep the needle a real string.

range

start / end must be non-negative

Negative offsets error. Start past the end of the string (or past end) returns -1.

Performance Notes

ApproachCostBest for
$indexOfBytesO(string length) per docShort strings; need a byte offset
+ start / endSmaller scanSkip a known prefix / window
$indexOfCPSimilar orderHuman character positions

Searching long text on every document adds up. Prefer $match first when only some rows need a lookup, and keep strings reasonably short when you call $indexOfBytes often.

Key Takeaways

  • Role: first UTF-8 byte index of a substring.
  • Not found: returns -1 (not an error).
  • Range: optional start / end are byte offsets.
  • Characters? use $indexOfCP instead of bytes.

One line: $indexOfBytes answers “at which UTF-8 byte does this substring start?”—or -1 if missing.

Frequently Asked Questions

$indexOfBytes returns the zero-based UTF-8 byte index of the first occurrence of a substring inside a string. Use it inside aggregation expression stages such as $project and $addFields.
Use { $indexOfBytes: [ <string>, <substring>, <start>, <end> ] }. start and end are optional byte offsets that limit the search range.
It returns -1 when the substring is not present in the string (within the optional start/end byte range).
$indexOfBytes counts UTF-8 bytes. $indexOfCP counts Unicode code points (characters). For ASCII-only text they match; for emoji or accented letters the indexes can differ.
If the string expression is null or refers to a missing field, $indexOfBytes returns null. A non-string (and not null) value causes an error. A null substring also errors.
Pass optional start (and optionally end) as non-negative byte offsets. Searching begins at start and stops before end.

Next: $indexOfCP

Find a substring using Unicode code-point indexes.

$indexOfCP 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