Skip to content

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 server
AerospikeClient client = new AerospikeClient("127.0.0.1", 3000);
// Creates a key with the namespace "test", set "users", and user key 1
Key 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, write
Record 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 trip
client.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:

SurfaceClassUsed withArgument order
OperationStringOperationclient.operate()Bin name first: StringOperation.strlen(binName, ctx...)
ExpressionStringExpExp.build(), ExpOperation.read(), filter policiesSource 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 name
StringOperation.upper(StringPolicy.Default, "text");
// Expression: policy, then source expression last
StringExp.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
FlagValueEffect
DEFAULT0Allow create or update.
CREATE_ONLY1Fail 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_ONLY2Silently no-op (bin not created) if the bin is missing. Valid on all modify operations. Mutually exclusive with CREATE_ONLY.
NO_FAIL4Suppress 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).

OperationReturnsDescription
strlenintegerCodepoint count.
byteLengthintegerUTF-8 byte count. Differs from strlen for non-ASCII text.
substrstringSubstring from a start index, or a [start, end) range.
charAtstringSingle codepoint at an index.
findintegerCodepoint index of needle, or a specific 1-based occurrence. -1 if absent.
containsbooleanWhether needle is a substring.
startsWithbooleanWhether the bin begins with prefix.
endsWithbooleanWhether the bin ends with suffix.
isNumericbooleanWhether the bin is a valid Integer or float, optionally filtered by StringNumericType.
isUpperbooleanWhether every cased codepoint is uppercase.
isLowerbooleanWhether every cased codepoint is lowercase.
regexComparebooleanWhether an ICU (International Components for Unicode) regex pattern matches, optionally with StringRegexFlags.
toIntegerintegerParse as an int64.
toDoublefloatParse as a double.
toBlobblobUTF-8 bytes of the string.
splitlistSplit by codepoint, or by a separator substring.
b64DecodeblobDecode 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.

OperationCREATE_ONLY valid?Description
insertYesSplice value in at a codepoint index.
overwriteYesOverwrite codepoints starting at an index. The resolved index must be in range, or the server returns a parameter error.
concatYesAppend one string, or each element of a list of strings, in order.
appendYesAppend value. Unicode-aware, unlike the legacy Operation.append.
prependYesPrepend value. Unicode-aware, unlike the legacy Operation.prepend.
padStartYesLeft-pad with padString up to targetLength codepoints. No-op if already at or above the target.
padEndYesRight-pad with padString up to targetLength codepoints.
repeatYesRepeat the bin count times.
snipNoRemove a [start, end) range, or truncate from start to the end.
replaceNoReplace the first occurrence of needle with replacement.
replaceAllNoReplace every occurrence of needle.
upper / lowerNoUppercase or lowercase the stored bin value.
caseFoldNoLocale-independent case fold, for comparison keys.
normalizeNFCNoNormalize to Unicode NFC (Normalization Form Composed).
trimStart / trimEnd / trimNoRemove Unicode whitespace from the start, end, or both.
regexReplaceNoReplace 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:

FlagApplies to
CASE_INSENSITIVEBoth
MULTILINEBoth
DOTALLBoth
UNIX_LINESBoth
GLOBALregexReplace 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 ClassCastException

Multiple 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 codepoints
Policy 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 bin
Record 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) and Operation.prepend(Bin) are deprecated for String bins only, in favor of StringOperation.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 using Operation.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 of StringExp.regexCompare(Exp, int, Exp), which is Unicode-aware. The legacy version uses POSIX regex semantics.

Next steps