String operations
For the complete documentation index see: llms.txt
All documentation pages available in markdown.
Server-side String operations let you search, transform, extract, and normalize text in a String bin without fetching the bin, editing it in your application, and writing it back.
This reference covers the Aerospike C client surface for developers already using aerospike_key_operate() and filter expressions: the as_operations_string_* functions for operate() calls, and the as_exp_string_* macros for expressions built with as_exp_build(). After reading this page, you can choose the right as_operations_string_* or as_exp_string_* call for a task, configure an as_string_policy, and interpret aerospike_key_operate() results, including multiple results for the same bin.
String operations require Aerospike Database 8.2.0 and later and Aerospike C client 7.6.2 and later. See Version requirements before using String operations against a production cluster.
For operation semantics, argument details, and the full 37-operation catalog, see the String operations reference and String expressions reference. To establish the cluster connection used in the Setup examples, see Connecting.
Setup
The examples on this page use the following connection and key:
#include <aerospike/aerospike.h>#include <aerospike/aerospike_key.h>#include <aerospike/as_error.h>#include <aerospike/as_key.h>#include <aerospike/as_operations.h>#include <aerospike/as_record.h>#include <aerospike/as_string_operations.h>
// Establishes a connection to the serveras_config config;as_config_init(&config);as_config_add_host(&config, "127.0.0.1", 3000);
aerospike as;aerospike_init(&as, &config);
as_error err;if (aerospike_connect(&as, &err) != AEROSPIKE_OK) { fprintf(stderr, "err(%d) %s at [%s:%d]\n", err.code, err.message, err.file, err.line);}
// Creates a key with the namespace "test", set "users", and user key "userId"as_key key;as_key_init_str(&key, "test", "users", "userId");Round-trip elimination
Without String operations, normalizing a bin takes a read, an application-side edit, and a write. A hand-rolled, byte-wise lowercase loop also handles only ASCII (American Standard Code for Information Interchange) codepoints:
// Before: fetch, modify (ASCII-only), writeas_record* rec = NULL;if (aerospike_key_get(&as, &err, NULL, &key, &rec) != AEROSPIKE_OK) { fprintf(stderr, "err(%d) %s at [%s:%d]\n", err.code, err.message, err.file, err.line);}
char* email = as_record_get_str(rec, "email");for (char* p = email; *p != '\0'; p++) { *p = tolower((unsigned char)*p); // breaks on non-ASCII codepoints}
if (aerospike_key_put(&as, &err, NULL, &key, rec) != AEROSPIKE_OK) { fprintf(stderr, "err(%d) %s at [%s:%d]\n", err.code, err.message, err.file, err.line);}
as_record_destroy(rec);as_operations_string_lower runs the same edit inside a single aerospike_key_operate() call, on the server, correctly for any Unicode codepoint (a single character unit in a string, which can span multiple bytes in UTF-8):
as_string_policy policy;as_string_policy_init(&policy);
// After: one round trip, Unicode-awareas_operations ops;as_operations_inita(&ops, 1);as_operations_string_lower(&ops, "email", NULL, &policy);
if (aerospike_key_operate(&as, &err, NULL, &key, &ops, NULL) != AEROSPIKE_OK) { fprintf(stderr, "err(%d) %s at [%s:%d]\n", err.code, err.message, err.file, err.line);}
as_operations_destroy(&ops);To confirm a modify operation applied as expected instead of assuming success from a non-error status alone, read the bin back:
as_record* rec = NULL;if (aerospike_key_get(&as, &err, NULL, &key, &rec) != AEROSPIKE_OK) { fprintf(stderr, "err(%d) %s at [%s:%d]\n", err.code, err.message, err.file, err.line);}else { printf("email = %s\n", as_record_get_str(rec, "email"));}
as_record_destroy(rec);Two surfaces
Every String operation is available in two forms:
| Surface | Naming | Used with | Argument order |
|---|---|---|---|
| Operation | as_operations_string_* | aerospike_key_operate() | Bin name first: as_operations_string_strlen(ops, name, ctx) |
| Expression | as_exp_string_* | as_exp_build(), as_operations_exp_read()/as_operations_exp_write(), as_policy_read.filter_exp and similar | Source expression last: as_exp_string_strlen(bin) |
as_operations_string_* functions read or modify a bin directly and return bool (whether the operation was added to the as_operations array). as_exp_string_* macros expand into as_exp_entry tokens that compose inside a larger as_exp_build() expression tree, with no separate build step for the sub-expression itself.
A modify-style as_exp_string_* macro (as_exp_string_upper, as_exp_string_replace, as_exp_string_trim, and similar) returns the transformed string as an expression value. Persist that value with as_operations_exp_write(), or call the matching as_operations_string_* function to write the bin directly. See Nested strings for a worked filter and projection example.
Every as_operations_string_* function also takes an optional as_cdt_ctx* ctx argument to reach a string nested inside a list or map. Pass NULL for ctx at the top level. See Nested strings for nested examples. On modify operations, the policy argument comes immediately after ctx, ahead of the operation’s own arguments (index, value, and similar):
// Operation: name, ctx, then policy, then the operation's own argumentsas_operations_string_upper(&ops, "text", NULL, &policy);
// Expression: policy first, source expression lastas_exp_string_upper(&policy, as_exp_bin_str("text"));String write policy
Modify operations take an as_string_policy, which wraps a bitmask of as_string_write_flags:
as_string_policy policy;as_string_policy_init(&policy); // AS_STRING_WRITE_FLAGS_DEFAULT (0)
as_string_policy custom;as_string_policy_init(&custom);as_string_policy_set(&custom, AS_STRING_WRITE_FLAGS_CREATE_ONLY | AS_STRING_WRITE_FLAGS_NO_FAIL); // combine with bitwise OR| Flag | Value | Effect |
|---|---|---|
AS_STRING_WRITE_FLAGS_DEFAULT | 0 | Allow create or update, subject to the operation’s own create capability (see the CREATE_ONLY column in Modify operations). |
AS_STRING_WRITE_FLAGS_CREATE_ONLY | 1 | Apply only if the bin doesn’t already exist. Fails with AEROSPIKE_ERR_BIN_EXISTS against a live bin. Valid only on the eight operations that can create a missing bin (insert, overwrite, concat, append, prepend, pad_start, pad_end, repeat). See the CREATE_ONLY column in Modify operations. Every other modify operation, and any operation carrying a ctx path, rejects it with AEROSPIKE_ERR_REQUEST_INVALID during argument parsing. |
AS_STRING_WRITE_FLAGS_UPDATE_ONLY | 2 | Apply only to an existing bin, disabling bin creation. Against a missing bin, the operation is a silent no-op, and the bin is not created. Valid on every modify operation. Mutually exclusive with CREATE_ONLY. Combining both returns AEROSPIKE_ERR_REQUEST_INVALID. |
AS_STRING_WRITE_FLAGS_NO_FAIL | 4 | Don’t raise an error when the modify itself can’t be applied. The operation becomes a silent success, and the bin keeps its unmodified prior value. Doesn’t suppress AEROSPIKE_ERR_BIN_INCOMPATIBLE_TYPE, ill-formed UTF-8, or the CREATE_ONLY argument-parsing rejections described in the AS_STRING_WRITE_FLAGS_CREATE_ONLY row. |
as_string_policy is a per-operation argument, not client configuration: build one with as_string_policy_init()/as_string_policy_set() and pass it to each call that needs non-default flags.
CREATE_ONLY, UPDATE_ONLY argument-parsing rejections, and most other argument errors return a non-AEROSPIKE_OK status from aerospike_key_operate() rather than failing silently. Check the status and inspect err.code to distinguish an expected condition from one you must propagate. See Error handling for the general as_error pattern used throughout this page:
as_string_policy create_only;as_string_policy_init(&create_only);as_string_policy_set(&create_only, AS_STRING_WRITE_FLAGS_CREATE_ONLY);
as_operations ops;as_operations_inita(&ops, 1);as_operations_string_insert(&ops, "email", NULL, &create_only, 0, "prefix-");
if (aerospike_key_operate(&as, &err, NULL, &key, &ops, NULL) != AEROSPIKE_OK) { if (err.code == AEROSPIKE_ERR_BIN_EXISTS) { // Expected: the bin already had a value, so CREATE_ONLY rejected the insert } else { fprintf(stderr, "err(%d) %s at [%s:%d]\n", err.code, err.message, err.file, err.line); }}
as_operations_destroy(&ops);In a multi-operation aerospike_key_operate() call, NO_FAIL only suppresses the affected operation. Sibling operations in the same call still commit, and the client receives no error either way. Read the bin back to confirm which operations applied rather than assuming success from a non-error status.
On every as_exp_string_* modify macro, including as_exp_string_regex_replace, only AS_STRING_WRITE_FLAGS_NO_FAIL is meaningful. CREATE_ONLY and UPDATE_ONLY are bin-existence predicates, so they don’t carry over to a source expression that may not be a bin at all. See the following caution about numeric flag values.
Read operations
Read operations take the bin name, an optional as_cdt_ctx* ctx, and the operation’s own arguments. Pass NULL for ctx at the top level, or a path to a value nested in a list or map, covered in Nested strings. In the following table, index and length arguments count Unicode codepoints, not bytes. Most characters are one codepoint, but some emoji and other characters outside the Unicode Basic Multilingual Plane (BMP, codepoints U+0000 through U+FFFF) are more than one codepoint.
| Operation | C functions | Returns | Description |
|---|---|---|---|
strlen | as_operations_string_strlen / as_exp_string_strlen | Integer | Codepoint count. |
byte_length | as_operations_string_byte_length / as_exp_string_byte_length | Integer | UTF-8 byte count. Differs from strlen for non-ASCII text. |
substr | as_operations_string_substr / _substr_range, as_exp_string_substr / _substr_range | String | Substring from a start index, or a half-open [start, end) range. |
char_at | as_operations_string_char_at / as_exp_string_char_at | String | Single codepoint at an index, as a one-codepoint string. |
find | as_operations_string_find / _find_occurrence, as_exp_string_find / _find_occurrence | Integer | Codepoint index of needle, or a specific 1-based occurrence. -1 if absent. |
contains | as_operations_string_contains / as_exp_string_contains | Boolean | Whether needle is a substring. |
starts_with | as_operations_string_starts_with / as_exp_string_starts_with | Boolean | Whether the bin begins with prefix. |
ends_with | as_operations_string_ends_with / as_exp_string_ends_with | Boolean | Whether the bin ends with suffix. |
is_numeric | as_operations_string_is_numeric / _is_numeric_type, as_exp_string_is_numeric / _is_numeric_type | Boolean | Whether the bin is a valid integer or float, optionally filtered by as_string_numeric_type. |
is_upper | as_operations_string_is_upper / as_exp_string_is_upper | Boolean | Whether every cased codepoint is uppercase. |
is_lower | as_operations_string_is_lower / as_exp_string_is_lower | Boolean | Whether every cased codepoint is lowercase. |
regex_compare | as_operations_string_regex_compare / _regex_compare_flags, as_exp_string_regex_compare / _regex_compare_flags | Boolean | Whether an ICU (International Components for Unicode) regex pattern matches, optionally with as_string_regex_flags. |
to_integer | as_operations_string_to_integer / as_exp_string_to_integer | Integer | Parse as an int64. Fails with AEROSPIKE_ERR_OP_NOT_APPLICABLE (AS_SUB_OPNOT_STRING_CONVERSION_FAILED, subcode 10) if unparsable. |
to_double | as_operations_string_to_double / as_exp_string_to_double | Float | Parse as a 64-bit float. Same failure subcode as to_integer if unparsable. |
to_blob | as_operations_string_to_blob / as_exp_string_to_blob | Blob | The UTF-8 bytes of the string, as a Blob. |
split | as_operations_string_split / _split_separator, as_exp_string_split / _split_separator | List | Splits by Unicode codepoint, or by separator (a singleton list if separator isn’t found). |
b64_decode | as_operations_string_b64_decode / as_exp_string_b64_decode | Blob | Decodes the bin as base64 text. Fails with AEROSPIKE_ERR_OP_NOT_APPLICABLE (AS_SUB_OPNOT_STRING_B64_INVALID, subcode 13) if it isn’t valid base64. |
Six read operations (contains, starts_with, ends_with, is_numeric, is_upper, is_lower) and regex_compare decode as as_boolean. See Reading operate results.
Modify operations
Modify operations write the transformed value back to the bin (as_operations_string_*) or return it as an expression value (as_exp_string_*). An expression-side modify macro does not mutate the underlying bin unless its result is persisted with as_operations_exp_write().
| Operation | CREATE_ONLY valid? | Description |
|---|---|---|
insert | Yes | Splice value in at a codepoint index. |
overwrite | Yes | Overwrite codepoints starting at an index. The result may grow beyond the original length when value extends past the end. |
concat | Yes | Append one string (as_operations_string_concat), or each element of an as_list of strings in order (_concat_list). |
append | Yes | Append value, with Unicode-aware concatenation. See Deprecations for the legacy byte-level alternative. |
prepend | Yes | Prepend value, with Unicode-aware concatenation. See Deprecations for the legacy byte-level alternative. |
pad_start | Yes | Left-pad with pad_string up to target_length codepoints. No-op if already at or above the target. |
pad_end | Yes | Right-pad with pad_string up to target_length codepoints. |
repeat | Yes | Repeat the bin count times. |
snip | No | Remove a [start, end) range (as_operations_string_snip), or truncate from start to the end (_snip_start, see the caution following this table). |
replace | No | Replace the first occurrence of needle with replacement. |
replace_all | No | Replace every occurrence of needle. |
upper / lower | No | Uppercase or lowercase the stored bin value. |
case_fold | No | Locale-independent case fold, for comparison keys. |
normalize_nfc | No | Normalize to Unicode NFC (Normalization Form Composed). Already-normalized strings are unchanged. |
trim_start / trim_end / trim | No | Remove Unicode whitespace from the start, end, or both. |
regex_replace | No | Replace regex pattern matches with replacement. Pass AS_STRING_REGEX_FLAGS_GLOBAL to replace every match instead of only the first. NO_FAIL also suppresses a regex compile failure. |
Only the eight operations marked “Yes” accept AS_STRING_WRITE_FLAGS_CREATE_ONLY. The server rejects it with AEROSPIKE_ERR_REQUEST_INVALID 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
as_operations_to_string/as_exp_to_string convert an integer, double, boolean, string, or blob bin to its string representation:
as_operations ops;as_operations_inita(&ops, 1);as_operations_to_string(&ops, "n");
as_record* rec = NULL;if (aerospike_key_operate(&as, &err, NULL, &key, &ops, &rec) != AEROSPIKE_OK) { fprintf(stderr, "err(%d) %s at [%s:%d]\n", err.code, err.message, err.file, err.line);}else { printf("%s\n", as_record_get_str(rec, "n"));}
as_operations_destroy(&ops);as_record_destroy(rec);as_operations_to_string is the only string operation that does not accept a ctx at all: its signature omits the parameter entirely, and it doesn’t send a MessagePack (msgpack) sub-operation payload. It is a distinct top-level wire operation that always reads the whole bin. It fails with AEROSPIKE_ERR_BIN_INCOMPATIBLE_TYPE for any other bin type, and with AEROSPIKE_ERR_OP_NOT_APPLICABLE (AS_SUB_OPNOT_STRING_UTF8_INVALID, subcode 11) for a blob bin whose bytes aren’t valid UTF-8.
To convert a value nested inside a list or map, extract the nested string first with the equivalent List/Map get operation (using the same ctx), then convert it client-side. Or compose as_exp_to_string with as_exp_list_get_by_index/as_exp_map_get_by_key inside an expression.
Regex and numeric-type flags
as_string_regex_flags (combine with bitwise OR) controls regex_compare and regex_replace:
| Flag | Applies to |
|---|---|
AS_STRING_REGEX_FLAGS_CASE_INSENSITIVE | Both |
AS_STRING_REGEX_FLAGS_MULTILINE | Both |
AS_STRING_REGEX_FLAGS_DOTALL | Both |
AS_STRING_REGEX_FLAGS_UNIX_LINES | Both |
AS_STRING_REGEX_FLAGS_GLOBAL | regex_replace only. Replaces every match instead of only the first. |
as_string_numeric_type narrows is_numeric_type: AS_STRING_NUMERIC_ANY (default), AS_STRING_NUMERIC_INT, or AS_STRING_NUMERIC_FLOAT. AS_STRING_NUMERIC_FLOAT requires a literal . followed by a digit, so is_numeric_type("5", AS_STRING_NUMERIC_FLOAT) is false even though "5" parses as a double.
Reading operate results
Booleans decode as as_boolean
contains, starts_with, ends_with, is_numeric, is_upper, is_lower, and regex_compare decode as as_boolean:
as_operations ops;as_operations_inita(&ops, 1);as_operations_string_contains(&ops, "email", NULL, "@");
as_record* rec = NULL;if (aerospike_key_operate(&as, &err, NULL, &key, &ops, &rec) != AEROSPIKE_OK) { fprintf(stderr, "err(%d) %s at [%s:%d]\n", err.code, err.message, err.file, err.line);}else { bool has_at = as_record_get_bool(rec, "email"); // correct // as_record_get_int64(rec, "email", 0) does not read the boolean correctly}
as_operations_destroy(&ops);as_record_destroy(rec);Multiple operations on one bin return multiple bin entries
When more than one operation in a single aerospike_key_operate() call targets the same bin, the C client does not group the results into one list-valued bin. Unlike the Java, Python, Go, C#, and Node.js clients, it returns separate entries: as_record.bins.entries contains one as_bin per operation, in submission order, each carrying the same bin name. This mirrors the equivalent CDT case already documented under Multiple Results for the Same Bin in Bin operations.
as_string_policy policy;as_string_policy_init(&policy);
as_operations ops;as_operations_inita(&ops, 2);as_operations_string_trim(&ops, "email", NULL, &policy); // modify: entry 0as_operations_add_read(&ops, "email"); // read: entry 1
as_record* rec = NULL;if (aerospike_key_operate(&as, &err, NULL, &key, &ops, &rec) != AEROSPIKE_OK) { fprintf(stderr, "err(%d) %s at [%s:%d]\n", err.code, err.message, err.file, err.line);}else { // rec->bins.size == 2 for this call as_bin* results = rec->bins.entries; const char* trimmed = as_string_get((as_string*)results[1].valuep);}
as_operations_destroy(&ops);as_record_destroy(rec);The modify operation still occupies an entry rather than being skipped, so results[1] (not results[0]) holds the read result shown in the preceding example.
A single string operation on a bin, with nothing else targeting that bin, populates exactly one as_bin entry, accessible directly with as_record_get_str()/as_record_get_int64()/as_record_get_bool() and similar.
Nested strings
as_operations_string_* functions take an optional as_cdt_ctx* ctx to reach a string nested inside a list or map. The path must already resolve to a string. A non-string leaf fails with AEROSPIKE_ERR_BIN_INCOMPATIBLE_TYPE.
// Uppercase a string nested in list bin "items" at index 0.as_string_policy policy;as_string_policy_init(&policy);
as_cdt_ctx ctx;as_cdt_ctx_init(&ctx, 1);as_cdt_ctx_add_list_index(&ctx, 0);
as_operations ops;as_operations_inita(&ops, 1);as_operations_string_upper(&ops, "items", &ctx, &policy);
if (aerospike_key_operate(&as, &err, NULL, &key, &ops, NULL) != AEROSPIKE_OK) { fprintf(stderr, "err(%d) %s at [%s:%d]\n", err.code, err.message, err.file, err.line);}
as_operations_destroy(&ops);as_cdt_ctx_destroy(&ctx);// Read strlen of a string nested under map key "bio".as_cdt_ctx bio_ctx;as_cdt_ctx_init(&bio_ctx, 1);as_cdt_ctx_add_map_key(&bio_ctx, (as_val*)as_string_new("bio", false));
as_operations ops;as_operations_inita(&ops, 1);as_operations_string_strlen(&ops, "profile", &bio_ctx);
as_record* rec = NULL;if (aerospike_key_operate(&as, &err, NULL, &key, &ops, &rec) != AEROSPIKE_OK) { fprintf(stderr, "err(%d) %s at [%s:%d]\n", err.code, err.message, err.file, err.line);}else { int64_t bio_len = as_record_get_int64(rec, "profile", 0);}
as_operations_destroy(&ops);as_record_destroy(rec);as_cdt_ctx_destroy(&bio_ctx); // also frees the as_string key: the ctx list takes ownership of itas_exp_string_* macros take no ctx at all. To apply a string expression to a nested value, project the value first with as_exp_list_get_by_index/as_exp_map_get_by_key (which do take a ctx, for further nesting), then pass the result as the source expression. The following example builds an as_exp_string_strlen condition on a map key, then uses it two ways: as a read filter, and as a projected read value.
This example also requires #include <aerospike/as_exp.h> and #include <aerospike/as_exp_operations.h>, in addition to the includes in Setup:
// bio_len > 280: is the bin's bio over 280 codepoints?as_exp_build(is_long, as_exp_cmp_gt( as_exp_string_strlen( as_exp_map_get_by_key(NULL, AS_MAP_RETURN_VALUE, AS_EXP_TYPE_STR, as_exp_str("bio"), as_exp_bin_map("profile"))), as_exp_int(280)));
// As a filter: fetch the record only if its bio is over 280 codepointsas_policy_read read_policy;as_policy_read_init(&read_policy);read_policy.filter_exp = is_long;
as_record* filtered = NULL;as_status status = aerospike_key_get(&as, &err, &read_policy, &key, &filtered);if (status != AEROSPIKE_OK && status != AEROSPIKE_FILTERED_OUT) { fprintf(stderr, "err(%d) %s at [%s:%d]\n", err.code, err.message, err.file, err.line);}if (filtered != NULL) { as_record_destroy(filtered);}
// As a projection: always fetch the record, with the condition's result in a computed binas_operations ops;as_operations_inita(&ops, 1);as_operations_exp_read(&ops, "isLong", is_long, AS_EXP_READ_DEFAULT);
as_record* projected = NULL;if (aerospike_key_operate(&as, &err, NULL, &key, &ops, &projected) != AEROSPIKE_OK) { fprintf(stderr, "err(%d) %s at [%s:%d]\n", err.code, err.message, err.file, err.line);}else { bool bio_is_long = as_record_get_bool(projected, "isLong");}
as_operations_destroy(&ops);as_record_destroy(projected);as_exp_destroy(is_long);as_operations_to_string/as_exp_to_string never accept ctx, on either surface. See Type conversion.
Version requirements
String operations require Aerospike Database 8.2.0 and later on every node, and Aerospike C client 7.6.2 and later. Aerospike Database 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
as_operations_add_append_str/_strpandas_operations_add_prepend_str/_strpare deprecated in Aerospike C client 7.5.0 in favor ofas_operations_string_append/as_operations_string_prepend, which are Unicode-aware (the legacy pair does a raw byte concatenation). No removal version is scheduled. - The legacy
as_operations_add_append_raw/_rawpandas_operations_add_prepend_raw/_rawpare not deprecated. They remain the only way to append or prepend a blob value, since the string module can’t target blob bins. For a blob bin, keep using these functions: the behavior is unchanged and fully supported. - The legacy
as_exp_cmp_regex(POSIX regex) is deprecated in Aerospike C client 7.5.0 in favor ofas_exp_string_regex_compare, which is Unicode-aware (ICU regex). No removal version is scheduled.
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
- Connecting: establishing the cluster connection used on this page
- Error handling: the
as_errorpattern used throughout this page - API reference (C)