MongoDB $indexOfCP Operator

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

What Is $indexOfCP?

$indexOfCP is a MongoDB aggregation expression that returns the zero-based Unicode code point index of the first match of a substring inside a string. Not found → -1. Think “character position,” not UTF-8 bytes.

Remember
$indexOfCP ≈ string indexOf, counted in code points

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

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

How to Use $indexOfCP in MongoDB

Pass the string, the substring to find, and optional code-point range bounds.

  1. Choose an expression stage ($addFields or $project).
  2. Pass [ "$field", "needle" ] to get the first code point index.
  3. Optionally add start (and end) as non-negative code point 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 code point 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          // code point offset
        to   = end or lengthCP(s)
        result = first code point 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{ $indexOfCP: [ string, substring, start?, end? ] }
UnitUnicode code points (characters)
FoundFirst matching code point index
Not found-1
Null / missing stringnull
vs $indexOfBytesCharacters vs UTF-8 bytes

Live Preview

String: "cafétéria". Code points count each letter once—even accented ones.

Simulates $indexOfCP with an optional start code point (same idea as the mongosh examples).

Result
text = "cafétéria"
search = "é", start = 0
$indexOfCP → 3

{ index: 3 }

Worked Walkthrough — cafétéria

Text "cafétéria". Code points count é as one character; UTF-8 bytes count it as two.

Calculation
text = "cafétéria"

code points:  c a f é t é r i a
indexes:      0 1 2 3 4 5 6 7 8

$indexOfCP(text, "é")  → 3   (first é)
$indexOfCP(text, "t")  → 4
$indexOfCP(text, "é", 4) → 5  (second é)
$indexOfCP(text, "e")  → -1  (plain "e" is different)

Contrast ($indexOfBytes):
  "t" is at byte 5 because é uses 2 UTF-8 bytes.
  Prefer $indexOfCP when you mean “character position.”

MongoDB Examples

We use a small messages collection with a text field. First we find a substring’s code point index, then we search from a start position. 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: "cafétéria" }
]);

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"cafétéria"String we will search (may include accents)
Picture
"cafétéria" as code points:

  index: 0 1 2 3 4 5 6 7 8
  chars: c a f é t é r i a

  first "é" → 3
  "t"       → 4
  plain "e" → -1

How It Works

  1. Insert once. insertMany adds three message documents.
  2. MongoDB stores Unicode as written. Accents like é are real characters in the string.
  3. find() shows the same shape. What you inserted is what you get back—ready for $indexOfCP next.

Example 2 — Find First Index

Goal: for each message, find where "a" first appears as a code point index.

mongosh
db.messages.aggregate([
  {
    $addFields: {
      a_at: {
        $indexOfCP: [ "$text", "a" ]
      }
    }
  }
]);

How It Works

  1. Read the stored string. "$text" is the message for each document.
  2. Search for “a”. $indexOfCP walks from code point 0.
  3. First hit wins. foo.bar.baz → 5 (the “a” in bar); cafétéria → 1.
  4. Not found → -1. hello world has no “a”, so a_at is -1.

Example 3 — Search From a Start Index

Same messages, but find "é" starting at code point 4. That skips the first é in "cafétéria".

mongosh
db.messages.aggregate([
  {
    $addFields: {
      e_acute_from_4: {
        $indexOfCP: [ "$text", "é", 4 ]
      }
    }
  }
]);

How It Works

  1. Same stored documents. Still using the three messages from Example 1.
  2. Start at code point 4. MongoDB ignores indexes 0–3 for this search.
  3. cafétéria. First é is at 3 (skipped); next é is at 5.
  4. Others stay -1. The ASCII messages have no é at all.

Edge Cases & Pitfalls

Check these before using $indexOfCP in production pipelines.

units

Code points ≠ bytes

Pair $indexOfCP with $substrCP. Do not mix its result with $substrBytes.

-1

-1 means not found

Do not treat -1 as a cut point. Branch with $cond when the substring might be missing.

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
$indexOfCPO(string length) per docCharacter positions; i18n text
+ start / endSmaller scanSkip a known prefix / window
$indexOfBytesSimilar orderUTF-8 byte offsets

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

Key Takeaways

  • Role: first code point index of a substring.
  • Not found: returns -1 (not an error).
  • Range: optional start / end are code point offsets.
  • Bytes? use $indexOfBytes instead of code points.

One line: $indexOfCP answers “at which character does this substring start?”—or -1 if missing.

Frequently Asked Questions

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

Next: $isArray

Check whether an expression resolves to an array.

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