String operations
For the complete documentation index see: llms.txt
All documentation pages available in markdown.
Jump to the Code block for a combined complete example.
String operations let the server search, transform, extract, and normalize text in a String bin. That avoids fetching a bin, editing it in your application, and writing it back.
This reference covers the Aerospike Node.js client surface for developers already using client.operate() and expressions: the Aerospike.strings builders for operate() calls, and the Aerospike.exp.string builders for expressions.
After reading this page, you can choose the right builder for a task, configure String write flags, and interpret operate() results.
It requires Aerospike Database 8.2.0 or later and Node.js client 7.0.0 or later.
See Version requirements before upgrading a production cluster.
For operation semantics, argument details, and the full 37-operation catalog, see the String operations reference and String expressions reference.
For installing or upgrading the aerospike package, see Installation.
Setup
The examples on this page use the following connection and key:
const Aerospike = await import("aerospike");const strings = Aerospike.strings;const stringExp = Aerospike.exp.string;const exp = Aerospike.exp;const map = Aerospike.maps;
// Set hosts to your server's address and portconst config = { hosts: "YOUR_HOST_ADDRESS:YOUR_PORT" };
// Creates a key with the namespace "sandbox", set "users", and user key "jdoe123"const key = new Aerospike.Key("sandbox", "users", "jdoe123");
// Establishes a connection to the serverconst client = await Aerospike.connect(config);Round-trip elimination
Without String operations, normalizing a bin takes a read, an application-side edit, and a write:
await client.put(key, { email: " Jane.Doe@Example.com " });
// Before: fetch, modify, writeconst before = await client.get(key);const email = before.bins.email.trim().toLowerCase();await client.put(key, { email });Aerospike.strings runs the same edit inside a single operate() call, on the server:
// After: one round tripconst ops = [strings.trim("email"), strings.lower("email")];await client.operate(key, ops);
// Verify: " Jane.Doe@Example.com " becomes "jane.doe@example.com"const after = await client.get(key);console.info(after.bins.email);Two surfaces
Every String operation is available in two forms:
| Surface | Naming | Used with | Argument order |
|---|---|---|---|
| Operation | Aerospike.strings.* | client.operate() | Bin name first: strings.strlen(binName) |
| Expression | Aerospike.exp.string.* | filterExpression or operation expressions | Source expression last: stringExp.strlen(bin) |
Use filterExpression in ReadPolicy/WritePolicy, or pass an Aerospike.exp.string builder to exp.operations.read/exp.operations.write.
Aerospike.strings builders return a StringOperation for client.operate().
Aerospike.exp.string builders return a plain array describing an expression node, which composes inside a larger expression tree with no separate build or compile step.
Assign the finished expression directly to filterExpression, or pass it to exp.operations.read/exp.operations.write.
A modify-style Aerospike.exp.string builder (stringExp.upper, stringExp.replace, stringExp.trim, and similar) returns the transformed string as a value.
It does not write the result back to the bin on its own.
To persist a modify expression’s result, write it back with exp.operations.write, or use the Aerospike.strings equivalent instead.
See Nested strings for a worked filter and projection example.
Modify operations also take a policy or flags argument first, ahead of the bin name or source expression.
On the operation surface, this means calling .withPolicy({ writeFlags }) on the returned StringOperation rather than passing it as an argument:
// Operation: chain .withPolicy() after the callstrings.upper("text").withPolicy({ writeFlags: strings.writeFlags.NO_FAIL });
// Expression: policy object first, source expression laststringExp.upper({ flags: strings.writeFlags.NO_FAIL }, exp.binStr("text"));String write policy
Aerospike.strings exposes its write flags, regex flags, and numeric-type filter as constants merged in from the native binding:
strings.writeFlags.DEFAULT; // 0strings.writeFlags.CREATE_ONLY; // 1strings.writeFlags.UPDATE_ONLY; // 2strings.writeFlags.NO_FAIL; // 4| Flag | Value | Effect |
|---|---|---|
DEFAULT | 0 | Allow create or update, subject to the operation’s own create capability (see Missing-bin behavior). |
CREATE_ONLY | 1 | Create the bin only when it’s missing. Valid only on the eight create-capable operations (see Missing-bin behavior). Fails with Aerospike.status.ERR_BIN_EXISTS if the bin already exists. Mutually exclusive with UPDATE_ONLY, and invalid together with a CDT context path (.withContext()). |
UPDATE_ONLY | 2 | Apply the operation only to an existing bin. Against a missing bin, this succeeds as a silent no-op rather than creating one or raising an error, even without NO_FAIL (see following caution). Mutually exclusive with CREATE_ONLY. |
NO_FAIL | 4 | Return success and leave the bin unchanged if the operation can’t be applied, instead of failing. Doesn’t suppress a wrong bin type or invalid UTF-8. |
strings.writeFlags exposes CREATE_ONLY the same way the Java, Python, and C# clients do.
Missing-bin behavior
Against a missing bin, DEFAULT never fails, but it also doesn’t make every operation create one.
Only 8 of the 19 modify operations can create a bin from nothing:
Calling one of those against a missing bin creates it, seeded from an empty string.
The other 11 modify operations (trim, upper, replace, and similar) can’t create a bin at all.
Against a missing bin, they leave the record unchanged and still return success, so a subsequent read of that bin returns undefined.
Read the bin back to confirm a write happened.
.withPolicy({ writeFlags }) is a per-operation call, not client configuration: there’s no string-policy field on ClientPolicy or OperatePolicy.
Chain it onto each StringOperation that needs non-default flags.
regexReplace takes regexFlags as a plain argument and write flags from .withPolicy(), like the other modify builders.
It accepts UPDATE_ONLY and NO_FAIL. CREATE_ONLY fails with Aerospike.status.ERR_REQUEST_INVALID.
See Regex and numeric-type flags.
Read operations
All read operations take the bin name as the first Aerospike.strings argument, or the source expression as the last Aerospike.exp.string argument.
Both also take an optional trailing collection data type (CDT) context (CDTContext) path to a value nested in a List or Map, covered in Nested strings.
Every operation requires the target to already be a String.
Calling one against another bin type fails with Aerospike.status.ERR_BIN_INCOMPATIBLE_TYPE (server status AS_ERR_INCOMPATIBLE_TYPE, code 12).
See Error codes and error handling.
Index and length values count Unicode codepoints (the standard unit for string indexes in these operations, not UTF-8 bytes).
Most characters are one codepoint, but some emoji span multiple codepoints for one visible character (a grapheme cluster, the character a reader perceives as a single unit).
Characters beyond U+FFFF, including many emoji and historic scripts, can also affect index and length counts.
Negative indexes count from the end of the string, and out-of-bounds indexes are clamped to the string’s length rather than raising an error.
regexCompare uses ICU regex syntax, the International Components for Unicode regex engine, which differs from JavaScript’s built-in RegExp in some constructs.
See Regex dialect for spellings that ICU rejects.
Substring matching in find, contains, startsWith, and endsWith (and in replace/replaceAll under Modify operations) treats canonically equivalent text as equal: different Unicode encodings of the same visual character compare as identical.
For example, a precomposed é (U+00E9) matches e followed by a combining acute accent (U+0301).
| Operation | Node.js builders | Returns | Description |
|---|---|---|---|
strlen | strings.strlen / stringExp.strlen | Integer | Codepoint count. |
byte_length | strings.byteLength / stringExp.byteLength | Integer | UTF-8 byte count. Differs from strlen for non-ASCII text. |
substr | strings.substr / strings.substrRange, and stringExp.substr (two- or three-arg) | String | Substring from start to the end, or the half-open range [start, end). |
char_at | strings.charAt / stringExp.charAt | String | The one-codepoint string at index. |
find | strings.find / strings.findOccurrence, and stringExp.find / stringExp.findOccurrence | Integer | Codepoint index of needle, or of a specific occurrence (1 = first, -1 = last). -1 if not found. |
contains | strings.contains / stringExp.contains | Boolean | Whether the bin contains needle. |
starts_with | strings.startsWith / stringExp.startsWith | Boolean | Whether the bin begins with prefix. |
ends_with | strings.endsWith / stringExp.endsWith | Boolean | Whether the bin ends with suffix. |
to_integer | strings.toInteger / stringExp.toInteger | Integer | Parses the string as a 64-bit integer. Fails if it doesn’t parse. See Type conversion. |
to_double | strings.toDouble / stringExp.toDouble | Float | Parses the string as a 64-bit float. Fails if it doesn’t parse. |
is_numeric | strings.isNumeric / strings.isNumericType, and stringExp.isNumeric / stringExp.isNumericType | Boolean | Whether the bin’s spelling matches an optional strings.numericType (ANY, INT, or FLOAT). |
is_upper / is_lower | strings.isUpper, strings.isLower / stringExp.isUpper, stringExp.isLower | Boolean | Whether every codepoint is a cased letter. Returns false if any digit, space, or punctuation is present, and true for an empty string. |
to_blob | strings.toBlob / stringExp.toBlob | Blob | The UTF-8 bytes of the string, as a Blob. |
split | strings.split / strings.splitSeparator, and stringExp.split / stringExp.splitSeparator | List | Splits by Unicode codepoint, or by separator (a singleton list if separator isn’t found). |
b64_decode | strings.b64Decode / stringExp.b64Decode | Blob | Decodes the bin as base64 text into a Blob. Fails if it isn’t valid base64. |
regex_compare | strings.regexCompare / strings.regexCompareFlags, and stringExp.regexCompare / stringExp.regexCompareFlags | Boolean | Matches an ICU regex pattern against the bin, optionally with strings.regexFlags. |
Seven read operations (contains, startsWith, endsWith, isNumeric, isUpper, isLower, regexCompare) return a native boolean, not an integer 0/1.
Their flag-carrying variants (isNumericType, regexCompareFlags) return the same boolean type.
See Reading operate() results.
Modify operations
Modify operations write a transformed value back to the bin (Aerospike.strings) or return it as an expression value (Aerospike.exp.string, which does not mutate the underlying bin).
Every modify operation accepts DEFAULT, UPDATE_ONLY, or NO_FAIL using .withPolicy().
CREATE_ONLY is valid only on the eight create-capable operations in this table’s first eight rows, which are also the only ones that can create a missing bin (see Missing-bin behavior).
| Operation | Node.js builders | Description |
|---|---|---|
insert | strings.insert / stringExp.insert | Splices value in at codepoint index. |
overwrite | strings.overwrite / stringExp.overwrite | Overwrites codepoints starting at index with value. |
concat | strings.concat / strings.concatList, and stringExp.concat / stringExp.concatList | Appends one string, or each element of a list of strings, in order. |
append | strings.append / stringExp.append | Appends value. Unicode-aware, unlike the legacy Aerospike.operations.append. |
prepend | strings.prepend / stringExp.prepend | Prepends value. Unicode-aware, unlike the legacy Aerospike.operations.prepend. |
pad_start | strings.padStart / stringExp.padStart | Left-pads with padString up to targetLength codepoints. No-op if already at or above the target. |
pad_end | strings.padEnd / stringExp.padEnd | Right-pads with padString up to targetLength codepoints. |
repeat | strings.repeat / stringExp.repeat | Repeats the bin count times. A count of 0 empties the bin instead of leaving it unchanged. See Destructive, irreversible writes. |
snip | strings.snip (deprecated alias strings.snipRange) / stringExp.snip (deprecated alias stringExp.snipRange); or strings.snipStart / stringExp.snipStart for the one-argument form | Removes the half-open range [start, end). With snipStart, or snip called with end omitted, removes from start through the end instead. |
replace | strings.replace / stringExp.replace | Replaces the first occurrence of needle with replacement. |
replace_all | strings.replaceAll / stringExp.replaceAll | Replaces every occurrence of needle with replacement. |
upper / lower | strings.upper, strings.lower / stringExp.upper, stringExp.lower | Uppercases or lowercases the bin. |
case_fold | strings.caseFold / stringExp.caseFold | Maps characters to a common case for locale-independent, case-insensitive comparison keys. |
normalize_nfc | strings.normalizeNfc / stringExp.normalizeNfc | Normalizes the bin to Unicode Normalization Form C (NFC), the canonical composed form. Already-normalized strings are unchanged. |
trim / trim_start / trim_end | strings.trim, strings.trimStart, strings.trimEnd / stringExp.trim, stringExp.trimStart, stringExp.trimEnd | Removes Unicode whitespace from both ends, the start, or the end. |
regex_replace | strings.regexReplace / stringExp.regexReplace | Replaces the first regex match, or every match when strings.regexFlags.GLOBAL is set. See String write policy. |
Type conversion
strings.toString/stringExp.toString converts an Integer, Float, Boolean, String, or Blob bin to its string representation.
It fails with Aerospike.status.ERR_BIN_INCOMPATIBLE_TYPE for any other bin type, and with Aerospike.status.ERR_OP_NOT_APPLICABLE (server status AS_ERR_OP_NOT_APPLICABLE, code 26) if a Blob bin’s bytes aren’t valid UTF-8.
const record = await client.operate(key, [strings.toString("age")]);console.info(record.bins.age);strings.toString is the only operation that does not accept a CDTContext.
It’s a separate server operation that always reads the whole bin and can’t carry a context path in its payload.
To convert a value nested inside a List or Map, extract the nested string first with a list or map get operation (using the same CDTContext), then convert it client-side.
Or compose stringExp.toString with exp.lists.getByIndex/exp.maps.getByKey inside an expression.
toInteger/toDouble parse failures and toString’s invalid-UTF-8 case both surface as Aerospike.status.ERR_OP_NOT_APPLICABLE, per the String operations error codes.
Regex and numeric-type flags
strings.regexFlags (combine with bitwise OR) controls regexCompareFlags and regexReplace:
| Flag | Value | Applies to |
|---|---|---|
NONE | 0 | Both |
CASE_INSENSITIVE | 1 | Both |
MULTILINE | 2 | Both |
DOTALL | 4 | Both |
UNIX_LINES | 8 | Both |
GLOBAL | 16 | regexReplace only. Replaces every match instead of only the first. |
strings.numericType narrows isNumericType: ANY (0, default from plain isNumeric), INT (1), or FLOAT (2).
FLOAT requires a literal . followed by a digit, so strings.isNumericType("bin", strings.numericType.FLOAT) against "5" returns false even though "5" parses as a double.
Reading operate() results
Booleans decode as booleans
contains, startsWith, endsWith, isNumeric, isNumericType, isUpper, isLower, regexCompare, and regexCompareFlags decode as a native JavaScript boolean, not an integer 0/1:
const record = await client.operate(key, [strings.contains("email", "@")]);console.info(record.bins.email); // true or falseMultiple operations on one bin return a list
When more than one operation in a single operate() call targets the same bin, the client groups that bin’s results into an array, with one entry per operation, in call order.
See Returning from operate() in Bin operations for the general grouping rule.
String operations follow it the same way CDT operations already do, with no extra policy needed.
Modify operations return no value of their own and don’t occupy an entry in the grouped array: the client’s own test suite confirms that chaining modify operations with a single read on the same bin returns that read’s value directly, with no array wrapper. Add a read operation for the same bin to retrieve the mutated value.
const ops = [ strings.trim("email"), // modify strings.strlen("email"), // read strings.substrRange("email", 0, 5), // read];
const record = await client.operate(key, ops);const results = record.bins.email; // array of the 2 read results, in order; trim's modify doesn't add an entryA single string operation on a bin, with nothing else targeting that bin, returns its value directly, with no array wrapper.
Nested strings
Aerospike.strings builders take an optional trailing CDT context (CDTContext), set with .withContext(), to reach a string nested inside a List or Map.
The path must already resolve to a string: a non-string nested value fails with Aerospike.status.ERR_BIN_INCOMPATIBLE_TYPE.
A path that doesn’t resolve — an out-of-bounds list index or a missing map key — fails with Aerospike.status.ERR_OP_NOT_APPLICABLE; set NO_FAIL to turn it into a success that writes nothing.
A malformed context path fails with Aerospike.status.ERR_REQUEST_INVALID (server status AS_ERR_PARAMETER, code 4) and isn’t suppressible by NO_FAIL.
See String operations context for the full model, which the server enforces identically for every client.
// Uppercase a string nested in a list bin "items" at index 0.await client.operate(key, [strings.upper("items").withContext((ctx) => ctx.addListIndex(0))]);
// Read strlen of a string nested under a map key.const record = await client.operate(key, [ strings.strlen("profile").withContext((ctx) => ctx.addMapKey("bio")),]);Aerospike.exp.string builders don’t take a CDTContext at all.
To apply a string expression to a nested value, project the value first with exp.lists.getByIndex/exp.maps.getByKey (which do take a context), then pass the result as the source expression.
The following example builds a stringExp.strlen condition, then uses it two ways: as a read filter, and as a projected read value.
const bioLen = stringExp.strlen( exp.maps.getByKey(exp.binMap("profile"), exp.str("bio"), exp.type.STR, map.returnType.VALUE),);const isLong = exp.gt(bioLen, exp.int(280));
// As a filter: fetch the record only if its bio is over 280 codepointsconst readPolicy = new Aerospike.ReadPolicy({ filterExpression: isLong });const record = await client.get(key, readPolicy);
// As a projection: always fetch the record, with the condition's result in a computed binconst projected = await client.operate(key, [ exp.operations.read("bioIsLong", isLong, exp.expReadFlags.DEFAULT),]);console.info(projected.bins.bioIsLong); // true or falsestrings.toString/stringExp.toString never accepts a CDTContext, on either surface.
See Type conversion.
Version requirements
String operations require Aerospike Database 8.2.0 or later on every node, and Node.js client 7.0.0 or later.
A server running a version prior to Database 8.2.0 doesn’t recognize the string opcodes and returns a generic parameter error, not a distinct “unsupported feature” code.
Check the cluster version rather than branching on the error.
Run asinfo -v build against each node to confirm it reports 8.2.0 or later.
Check the installed aerospike package version with npm list aerospike to confirm the client version, and see Installation to install or upgrade it.
Deprecations
- The legacy
Aerospike.operations.append(bin)andAerospike.operations.prepend(bin)are deprecated for String bins only, in favor ofstrings.append/strings.prepend, which are Unicode-aware. The legacy pair does a raw byte concatenation and doesn’t support string write flags orCDTContext. - Both legacy operations also accept Blob bins, which the string module can’t target.
For a Blob bin, keep using
Aerospike.operations.append/Aerospike.operations.prepend. There’s no string-module replacement, and the legacy behavior is unchanged and fully supported for that case. - The legacy
exp.cmpRegex(options, regex, cmpStr)(POSIX regex, perregex.h) is deprecated for string matching in favor ofstringExp.regexCompare/stringExp.regexCompareFlags, which are Unicode-aware (ICU regex). Seestring_regex_compare. strings.snipRange/stringExp.snipRangeare deprecated aliases forsnipwith the same behavior: both dispatch to the same underlying native call. On the expression surface,stringExp.substrRangeis a deprecated alias for the three-argumentstringExp.substr(start, end, bin). On the operation surface,strings.substrRangeis a distinct, non-deprecated function.
Migrating from the legacy byte-concatenation APIs
Switching a bin’s write path from Aerospike.operations.append/Aerospike.operations.prepend to strings.append/strings.prepend starts UTF-8 validation on that bin; the legacy byte-concatenation path never validated UTF-8 at all.
Before switching write paths in production:
- Audit affected bins for valid UTF-8 and repair any that fail (see Repair legacy String bins).
- Roll out the switch per namespace or set, not all at once.
- Monitor for the
ERR_OP_NOT_APPLICABLE/OPNOT_STRING_UTF8_INVALIDpair after each rollout step. - If it appears in production, revert that bin’s write path to the legacy
Aerospike.operations.append/Aerospike.operations.prependcall while you complete the repair.
Code block
Expand this section for a single code block combining round-trip elimination, a modify operation with flags, and reading a boolean result.
const Aerospike = await import("aerospike");const strings = Aerospike.strings;
// Set hosts to your server's address and portconst config = { hosts: "YOUR_HOST_ADDRESS:YOUR_PORT" };
// Creates a key with the namespace "sandbox", set "users", and user key "jdoe123"const key = new Aerospike.Key("sandbox", "users", "jdoe123");
// Establishes a connection to the serverconst client = await Aerospike.connect(config);
await client.put(key, { email: " Jane.Doe@Example.com " });
// Normalize the email in one round trip instead of get/edit/putconst ops = [ strings.trim("email"), strings.lower("email").withPolicy({ writeFlags: strings.writeFlags.NO_FAIL }),];await client.operate(key, ops);
// A single operation on a bin returns its value directly (no array wrapper)const record = await client.operate(key, [strings.contains("email", "@")]);console.info("Contains '@':", record.bins.email); // true
await client.close();Next steps
- Bin operations for the general
operate()and result-grouping model String operations build on. - Expressions for filter expressions and operation expressions beyond
Aerospike.exp.string. - String operations reference and String expressions reference for full operation semantics, argument details, and error subcodes.
- String for the underlying data type and Unicode model.
- Error handling for the
AerospikeErrorshape and status codes used throughout this page.