String Expression Operators
String expression operators format and compare text inside aggregation pipelines. They do not change stored documents unless you write results back with $merge or an update.
String operators in aggregation
String expression operators format and compare text inside aggregation pipelines. They do not change stored documents unless you write results back with $merge or an update.
Real-life example: These operators are like a mail-merge tool — combine first and last name, uppercase a coupon code, or compare two emails case-insensitively.
- $concat — join strings
- $toUpper / $toLower — change case
- $strcasecmp — compare two strings (case-insensitive, returns −1/0/1)
- $substrCP — slice by Unicode code points (safe for emoji and Devanagari)
$concat, $toUpper, and $toLower
$concat joins any number of strings and coerces non-strings where possible.
$toUpper and $toLower normalize case for display or matching.
Real-life example: $concat builds a display label 'Asha Kumar — Grade 10' from separate fields for a Rishtaara report card PDF.
db.students.aggregate([
{
$project: {
fullLabel: {
$concat: ["$firstName", " ", "$lastName", " — Grade ", { $toString: "$grade" }]
},
emailLower: { $toLower: "$email" },
couponNormalized: { $toUpper: "$couponCode" }
}
}
])
// Mask part of a phone for admin UI
db.users.aggregate([
{
$addFields: {
phoneMasked: {
$concat: [
{ $substrCP: ["$phone", 0, 2] },
"****",
{ $substrCP: ["$phone", 6, 4] }
]
}
}
}
])$strcasecmp — compare without case
$strcasecmp returns 0 if strings are equal ignoring case, −1 if first is less, 1 if first is greater (lexicographic).
Prefer $strcasecmp over $toLower equality when sorting or branching in $cond.
Real-life example: $strcasecmp checks whether two signup emails are the same even if one user typed ASHA@Gmail.com.
db.signups.aggregate([
{
$addFields: {
emailNormalized: { $toLower: "$email" }
}
},
{
$group: {
_id: "$emailNormalized",
count: { $sum: 1 },
names: { $push: "$name" }
}
},
{ $match: { count: { $gt: 1 } } }
])
// Branch on string compare
db.products.aggregate([
{
$addFields: {
tier: {
$cond: {
if: { $eq: [{ $strcasecmp: ["$category", "premium"] }, 0] },
then: "A",
else: "B"
}
}
}
}
])$substrCP — safe substring
$substrCP(start, length) slices by Unicode code points — unlike legacy $substr which broke multi-byte characters.
Use it for initials, SKU prefixes, and fixed-width codes.
Real-life example: $substrCP takes the first two characters of a city name for a sort key — works for 'Mumbai' and emoji-rich social handles alike.
db.students.aggregate([
{
$project: {
name: 1,
initials: {
$concat: [
{ $substrCP: ["$firstName", 0, 1] },
".",
{ $substrCP: ["$lastName", 0, 1] },
"."
]
},
cityCode: { $substrCP: ["$address.city", 0, 3] }
}
}
])
// Group sales by SKU prefix (first 4 chars)
db.sales.aggregate([
{
$group: {
_id: { $substrCP: ["$sku", 0, 4] },
revenue: { $sum: "$amount" }
}
},
{ $sort: { revenue: -1 } }
])