String operations
For the complete documentation index see: llms.txt
All documentation pages available in markdown.
String operations let the server search, transform, extract, and normalize text in a String bin. This approach avoids fetching a bin, editing it in your application, and writing it back. This reference covers the Java client surface, for Java developers already using client.operate() and expressions: StringOperation for operate() calls and StringExp for expressions. It requires Aerospike Database 8.2.0 or later and Java client 10.4.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.
Setup
The examples on this page use the following connection and key:
import com.aerospike.client.AerospikeClient;import com.aerospike.client.Bin;import com.aerospike.client.Key;import com.aerospike.client.Record;
// Establishes a connection to the serverAerospikeClient client = new AerospikeClient("127.0.0.1", 3000);
// Creates a key with the namespace "test", set "users", and user key 1Key key = new Key("test", "users", 1);Round-trip elimination
Without String operations, normalizing a bin takes a read, an application-side edit, and a write:
// Before: fetch, modify, writeRecord record = client.get(null, key);String email = record.getString("email").strip().toLowerCase();client.put(null, key, new Bin("email", email));StringOperation runs the same edit inside a single operate() call, on the server:
import com.aerospike.client.operation.StringOperation;import com.aerospike.client.operation.StringPolicy;
// After: one round tripclient.operate(null, key, StringOperation.trim(StringPolicy.Default, "email"), StringOperation.lower(StringPolicy.Default, "email"));
// Verify: " Jane.Doe@Example.com " becomes "jane.doe@example.com"Record verify = client.get(null, key);System.out.println(verify.getString("email"));Two surfaces
Every String operation is available in two forms, in packages com.aerospike.client.operation and com.aerospike.client.exp:
| Surface | Class | Used with | Argument order |
|---|---|---|---|
| Operation | StringOperation | client.operate() | Bin name first: StringOperation.strlen(binName, ctx...) |
| Expression | StringExp | Exp.build(), ExpOperation.read(), filter policies | Source expression last: StringExp.strlen(src) |
StringOperation builders read or modify a bin directly. StringExp builders produce an Exp node that composes inside a larger expression. A modify-style StringExp (upper, replace, trim, and similar) returns the transformed string as a value and does not write it back to the bin on its own. To persist a modify expression’s result, write it back with Operation.put/ExpOperation.write, or use the StringOperation equivalent instead. See Nested strings for a worked filter and projection example.
Modify operations also take a policy as their first argument, ahead of the bin name:
// Operation: policy, then bin nameStringOperation.upper(StringPolicy.Default, "text");
// Expression: policy, then source expression lastStringExp.upper(StringPolicy.Default, Exp.stringBin("text"));String write policy
Modify operations take a StringPolicy, which wraps a bitmask of StringWriteFlags:
import com.aerospike.client.operation.StringPolicy;import com.aerospike.client.operation.StringWriteFlags;
StringPolicy policy = StringPolicy.Default; // DEFAULT (0)StringPolicy custom = new StringPolicy( StringWriteFlags.CREATE_ONLY | StringWriteFlags.NO_FAIL); // combine with bitwise OR| Flag | Value | Effect |
|---|---|---|
DEFAULT | 0 | Allow create or update. |
CREATE_ONLY | 1 | Fail with BIN_EXISTS_ERROR if the bin already exists. Valid only on the eight operations that can create a missing bin: insert, overwrite, concat, append, prepend, padStart, padEnd, repeat. Every other modify operation rejects it with PARAMETER_ERROR. |
UPDATE_ONLY | 2 | Silently no-op (bin not created) if the bin is missing. Valid on all modify operations. Mutually exclusive with CREATE_ONLY. |
NO_FAIL | 4 | Suppress errors raised while the operation runs, leaving the bin at its prior value. Does not suppress a wrong bin type, invalid UTF-8, or argument-parsing errors like an invalid flag combination. |
StringPolicy is a per-operation argument, not client configuration: there is no stringPolicyDefault on ClientPolicy. Pass a new StringPolicy to each call that needs non-default flags.
CREATE_ONLY, UPDATE_ONLY, and most argument errors raise an AerospikeException rather than failing silently. Catch it and inspect the result code to distinguish an expected condition from one you need to propagate:
import com.aerospike.client.AerospikeException;import com.aerospike.client.ResultCode;
try { client.operate(null, key, StringOperation.insert(new StringPolicy(StringWriteFlags.CREATE_ONLY), "email", 0, "prefix-"));} catch (AerospikeException ae) { if (ae.getResultCode() == ResultCode.BIN_EXISTS_ERROR) { // Expected: the bin already had a value, so CREATE_ONLY rejected the insert } else { throw ae; }}In a multi-operation operate() call, NO_FAIL only suppresses the affected operation. Sibling operations in the same call still commit, and the client receives no exception either way. Read the bin back to confirm which operations applied rather than assuming success from the absence of an error. NO_FAIL also suppresses a result that would exceed the per-operation size cap: the operation returns success and leaves the bin unchanged, which looks identical to success from the client.
On StringExp, only NO_FAIL is meaningful for most modify builders. CREATE_ONLY and UPDATE_ONLY only make sense when the target is a bin, so they don’t carry over to a source expression that may not be a bin at all. regexReplace is the exception: it accepts DEFAULT, UPDATE_ONLY, and NO_FAIL like its StringOperation counterpart, and rejects CREATE_ONLY. See the “Two flag families collide numerically” caution later on this page, under Modify operations.
Read operations
All read operations take the bin name (and an optional CTX path to a value nested in a List or Map, covered in Nested strings) as StringOperation arguments, or the source expression as the last StringExp argument. Index and length values below count Unicode codepoints, not bytes: most characters are one codepoint, but a codepoint can differ from a UTF-16 char for characters outside the Basic Multilingual Plane (for example, some emoji).
| Operation | Returns | Description |
|---|---|---|
strlen | integer | Codepoint count. |
byteLength | integer | UTF-8 byte count. Differs from strlen for non-ASCII text. |
substr | string | Substring from a start index, or a [start, end) range. |
charAt | string | Single codepoint at an index. |
find | integer | Codepoint index of needle, or a specific 1-based occurrence. -1 if absent. |
contains | boolean | Whether needle is a substring. |
startsWith | boolean | Whether the bin begins with prefix. |
endsWith | boolean | Whether the bin ends with suffix. |
isNumeric | boolean | Whether the bin is a valid Integer or float, optionally filtered by StringNumericType. |
isUpper | boolean | Whether every cased codepoint is uppercase. |
isLower | boolean | Whether every cased codepoint is lowercase. |
regexCompare | boolean | Whether an ICU (International Components for Unicode) regex pattern matches, optionally with StringRegexFlags. |
toInteger | integer | Parse as an int64. |
toDouble | float | Parse as a double. |
toBlob | blob | UTF-8 bytes of the string. |
split | list | Split by codepoint, or by a separator substring. |
b64Decode | blob | Decode the bin as base64 text. |
Six read operations (contains, startsWith, endsWith, isNumeric, isUpper, isLower) and regexCompare return a native boolean, not an integer 0/1. See Reading operate results.
Modify operations
Modify operations write the transformed value back to the bin (StringOperation) or return it as an expression value (StringExp). An expression-side modify builder does not mutate the underlying bin.
| Operation | CREATE_ONLY valid? | Description |
|---|---|---|
insert | Yes | Splice value in at a codepoint index. |
overwrite | Yes | Overwrite codepoints starting at an index. The resolved index must be in range, or the server returns a parameter error. |
concat | Yes | Append one string, or each element of a list of strings, in order. |
append | Yes | Append value. Unicode-aware, unlike the legacy Operation.append. |
prepend | Yes | Prepend value. Unicode-aware, unlike the legacy Operation.prepend. |
padStart | Yes | Left-pad with padString up to targetLength codepoints. No-op if already at or above the target. |
padEnd | Yes | Right-pad with padString up to targetLength codepoints. |
repeat | Yes | Repeat the bin count times. |
snip | No | Remove a [start, end) range, or truncate from start to the end. |
replace | No | Replace the first occurrence of needle with replacement. |
replaceAll | No | Replace every occurrence of needle. |
upper / lower | No | Uppercase or lowercase the stored bin value. |
caseFold | No | Locale-independent case fold, for comparison keys. |
normalizeNFC | No | Normalize to Unicode NFC (Normalization Form Composed). |
trimStart / trimEnd / trim | No | Remove Unicode whitespace from the start, end, or both. |
regexReplace | No | Replace regex pattern matches with replacement. Pass StringRegexFlags.GLOBAL to replace every match. |
Only the eight operations marked “Yes” accept StringWriteFlags.CREATE_ONLY. The server rejects it with PARAMETER_ERROR on every other modify operation, and on any operation that carries a CTX (nested) path. UPDATE_ONLY and NO_FAIL are valid on all of them.
Type conversion
toString converts an integer, float, boolean, string, or blob bin to its string representation:
Record record = client.operate(null, key, StringOperation.toString("n"));String s = record.getString("n");toString is the only operation that does not accept a CTX. It is a separate server operation that always reads the whole bin and cannot carry a context path in its payload. To convert a value nested inside a List or Map, extract the nested string first with ListOperation.getByIndex/MapOperation.getByKey (using the same CTX), then convert it client-side. Or compose StringExp.toString with ListExp.getByIndex/MapExp.getByKey inside an expression.
Regex and numeric-type flags
StringRegexFlags (combine with bitwise OR) controls regexCompare and regexReplace:
| Flag | Applies to |
|---|---|
CASE_INSENSITIVE | Both |
MULTILINE | Both |
DOTALL | Both |
UNIX_LINES | Both |
GLOBAL | regexReplace only. Replaces every match instead of only the first. |
StringNumericType narrows isNumeric: ANY (default), INT, or FLOAT. FLOAT requires a literal . followed by a digit, so isNumeric("5", StringNumericType.FLOAT) is false even though "5" parses as a double.
Reading operate results
Booleans decode as booleans
contains, startsWith, endsWith, isNumeric, isUpper, isLower, and regexCompare decode as a native boolean, not an integer 0/1:
Record record = client.operate(null, key, StringOperation.contains("email", "@"));boolean hasAt = record.getBoolean("email"); // correct// record.getLong("email") throws a ClassCastExceptionMultiple operations on one bin return a list
When more than one operation in a single operate() call targets the same bin, the server returns an ordered list in that bin’s result, one entry per operation in submission order. Modify operations contribute an empty entry rather than being skipped. See Returning from operate() in Bin operations.
import java.util.List;
Record record = client.operate(null, key, StringOperation.trim(StringPolicy.Default, "email"), // modify: empty entry StringOperation.strlen("email"), // read: entry 1 StringOperation.substr("email", 0, 5)); // read: entry 2
List<?> results = record.getList("email");long len = (Long) results.get(1);String head = (String) results.get(2);A single string operation on a bin, with nothing else targeting that bin, returns its value directly, with no list wrapper.
Nested strings
StringOperation takes an optional trailing CTX... 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 AS_ERR_INCOMPATIBLE_TYPE.
import com.aerospike.client.Value;import com.aerospike.client.cdt.CTX;
// Uppercase a string nested in a list bin "items" at index 0.client.operate(null, key, StringOperation.upper(StringPolicy.Default, "items", CTX.listIndex(0)));
// Read strlen of a string nested under a map key.Record record = client.operate(null, key, StringOperation.strlen("profile", CTX.mapKey(Value.get("bio"))));StringExp builders do not take a CTX at all. To apply a string expression to a nested value, project the value first with ListExp.getByIndex/MapExp.getByKey (which do take CTX), then pass the result as the src argument. The following example builds a StringExp condition, then uses it two ways: as a read filter, and as a projected read value.
import com.aerospike.client.exp.Exp;import com.aerospike.client.exp.MapExp;import com.aerospike.client.exp.StringExp;import com.aerospike.client.exp.ExpOperation;import com.aerospike.client.exp.ExpReadFlags;import com.aerospike.client.cdt.MapReturnType;import com.aerospike.client.policy.Policy;
Exp bio = MapExp.getByKey(MapReturnType.VALUE, Exp.Type.STRING, Exp.val("bio"), Exp.mapBin("profile"));Exp isLong = Exp.gt(StringExp.strlen(bio), Exp.val(280));
// As a filter: fetch the record only if its bio is over 280 codepointsPolicy policy = new Policy();policy.filterExp = Exp.build(isLong);Record filtered = client.get(policy, key); // null if the filter excludes the record
// As a projection: always fetch the record, with the condition's result in a computed binRecord projected = client.operate(null, key, ExpOperation.read("isLong", Exp.build(isLong), ExpReadFlags.DEFAULT));boolean bioIsLong = projected.getBoolean("isLong");toString never accepts CTX, on either surface. See Type conversion.
Version requirements
String operations require Aerospike Database 8.2.0 or later on every node, and Java client 10.4.0 or later. A server prior to 8.2.0 does not 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.
Deprecations
- The legacy
Operation.append(Bin)andOperation.prepend(Bin)are deprecated for String bins only, in favor ofStringOperation.append/StringOperation.prepend, which are Unicode-aware (the legacy pair does a raw byte concatenation). Both legacy operations also accept Blob bins, which the string package cannot target. For a Blob bin, keep usingOperation.append/Operation.prepend: there is no string-module replacement, and the legacy behavior is unchanged and fully supported for that case. - The legacy
Exp.regexCompare(String, int, Exp)is deprecated in favor ofStringExp.regexCompare(Exp, int, Exp), which is Unicode-aware. The legacy version uses POSIX regex semantics.
Next steps
- String operations reference: full semantics, index-bounds behavior, and error codes for all 37 operations
- String expressions reference
- String examples
- String operations and UTF-8 validation
- Bin operations
- Expressions - Java: building and using
Exp, filter policies, andExpOperation - API reference (Java)