$substrCP is a MongoDB aggregation expression that returns a substring by Unicode code-point index and count. Accents and most emoji count as one unit each, so it is the safe choice for display truncation. For byte budgets, use $substrBytes.
For “rest of string,” compute remaining code points with $strLenCP and $subtract.
Piece
Required?
Role
String
Yes
Source text (field or literal)
Code-point index
Yes
Zero-based character start (≥ 0)
Code-point count
Yes
How many characters to take (≥ 0)
Pseudocode
Pseudocode
for each document:
s = evaluate(string)
i = evaluate(codePointIndex)
n = evaluate(codePointCount)
if s is null or missing:
result = ""
else if i or n is negative:
error
else:
result = codePoints(s) from i for n characters
emit { ..., field: result }
Reference
Cheat Sheet
Keep these facts handy while slicing by code points.
Topic
Detail
Type
Aggregation expression (not a find filter)
Syntax
{ $substrCP: [ <string>, <index>, <count> ] }
Units
Unicode code points (characters)
Null / missing
Returns ""
Rest of string
$substrCP([s, i, { $subtract: [ { $strLenCP: s }, i ] } ])
vs $substrBytes
Characters vs UTF-8 bytes
Sample check
$substrCP(["café", 0, 3]) → "caf"
Try it
Live Preview
Watch $substrCP cut by characters. Try ASCII and accented text where one letter can still be one code point.
Simulates $substrCP (same idea as the mongosh examples below).
The word café has 4 code points. We take a 3-character preview, then the remaining letter é.
Calculation
String: "café"
Index: 0 1 2 3
Chars: c a f é
Preview (3 code points from 0):
$substrCP(["$title", 0, 3]) → "caf"
Rest (length − 3 from index 3):
len = $strLenCP("$title") → 4
count = 4 − 3 → 1
$substrCP(["$title", 3, 1]) → "é"
Check: "caf" + "é" = "café" ✓
Note: $substrBytes mid-slice can error; $substrCP stays character-safe
Code
MongoDB Examples
We store one title café. First we extract a character preview, then the rest using $strLenCP. Run these in mongosh after inserting the sample data once.
Example 1 — Sample Data
Imagine one post with title café. We save it in a collection named posts.
mongosh
db.posts.insertOne({
_id: 1,
title: "café"
});
How the Data Is Stored in MongoDB
After the insert, MongoDB keeps one document in posts. Run find() to see it:
mongosh
db.posts.find()
Stored in MongoDB
[
{
_id: 1,
title: "café"
}
]
Read that document like this:
Field in MongoDB
Value
Plain English
_id
1
Document id for this lesson
title
"café"
4 code points; é is still one character
Picture
title code points: c a f é
index: 0 1 2 3
indexes 0–2 → "caf" preview
index 3 → "é" rest
How It Works
Insert once.insertOne adds a single post document.
Accent stays one character.é is one code point even if it uses two UTF-8 bytes.
find() shows the same shape. What you inserted is what you get back—ready for $substrCP next.
Example 2 — Character Preview
Goal: take the first three code points of title as a short preview ("caf").
Slice from index 3. One code point yields "é"—kept whole, not split mid-byte.
Edge Cases & Pitfalls
Check these before using $substrCP in production pipelines.
vs bytes
Not the same as $substrBytes
$substrBytes can error on mid-character cuts. $substrCP thinks in characters and is safer for UI previews.
count
Index and count must be ≥ 0
There is no negative-length shortcut. Use $strLenCP − index for the remainder.
null
Null becomes empty string
Null or missing string input returns "", not an error. Non-string values still fail.
grapheme
Code points ≠ grapheme clusters
Some emoji or combined marks are multiple code points. $substrCP counts code points, not always what a user perceives as one glyph.
Analysis
Performance Notes
Approach
Cost
Best for
$substrCP alone
Per-document Unicode slice
Previews, display truncation
+ $strLenCP
Length + slice
Dynamic “rest of string”
$substrBytes
Similar cost, different units
Storage / byte budgets
Character slicing is cheap for short titles. Filter early with $match when only some documents need a preview.
Remember
Key Takeaways
Role: substring by Unicode code-point index + count.
Use when: display / character-safe truncation.
Rest: use $strLenCP − start.
Bytes: use $substrBytes instead.
One line:$substrCP answers “which characters do I get from this start for this count?”—without cutting accents or emoji mid-byte.
Frequently Asked Questions
$substrCP returns part of a string starting at a zero-based Unicode code-point index for a given code-point count. Code points correspond to characters, so accents and most emoji count as one unit each. Use it inside aggregation expression stages such as $project and $addFields.
Use { $substrCP: [ <string>, <code point index>, <code point count> ] }. Both index and count must resolve to non-negative integers.
$substrCP slices by Unicode code points (characters). $substrBytes slices by UTF-8 storage bytes. Prefer $substrCP for display truncation; prefer $substrBytes for storage or byte budgets.
Prefer $substrCP whenever character boundaries matter—accents, emoji, or international text. Legacy $substr is an alias of $substrBytes and can break multi-byte characters.
If the string is null or the field is missing, $substrCP returns an empty string "". Non-string values cause an error.
Compute remaining code points with $subtract and $strLenCP, then pass that count to $substrCP. Index and count must stay non-negative.