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. That avoids fetching a bin, editing it in your application, and writing it back.
This reference covers the Aerospike C# client surface for developers already using client.Operate() and Expressions - C#: the StringOperation builders for Operate() calls, and the StringExp builders for expressions. After reading this page, you can choose the right builder for a task, configure a StringPolicy, and interpret Operate() results.
It requires Aerospike Database 8.2.0 or later and Aerospike C# client 8.5.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:
using Aerospike.Client;using System.Collections;
// Define host configurationHost config = new Host("127.0.0.1", 3000);// Establishes a connection to the serverAerospikeClient client = new AerospikeClient(null, config);
// Creates a key with the namespace "sandbox", set "users", and user key "jdoe123"Key key = new Key("sandbox", "users", "jdoe123");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").Trim().ToLower();client.Put(null, key, new Bin("email", email));StringOperation runs the same edit inside a single Operate call, on the server:
StringPolicy policy = StringPolicy.Default;
// After: one round tripclient.Operate(null, key, StringOperation.Trim(policy, "email"), StringOperation.Lower(policy, "email"));
// Verify: " Jane.Doe@Example.com " becomes "jane.doe@example.com"Record record = client.Get(null, key);Console.WriteLine(record.GetString("email"));Two surfaces
Every String operation is available in two forms:
| Surface | Naming | Used with | Argument order |
|---|---|---|---|
| Operation | StringOperation.* | client.Operate() | Bin name first: StringOperation.Strlen(binName, ctx...) |
| Expression | StringExp.* | Policy.filterExp/WritePolicy.filterExp (using Exp.Build(...)), operation expressions (ExpOperation.Read/ExpOperation.Write) | Source expression last: StringExp.Strlen(src) |
StringOperation builders read or modify a bin directly, returning an Operation for client.Operate(). StringExp builders return an Exp node that composes inside a larger expression tree. Wrap the finished tree with Exp.Build(...) to get the Expression that Policy.filterExp/WritePolicy.filterExp and ExpOperation.Read/ExpOperation.Write expect.
A modify-style StringExp 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 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 or source expression:
// 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 StringWriteFlags value:
StringPolicy policy = StringPolicy.Default; // DEFAULT (0)StringPolicy custom = new StringPolicy(StringWriteFlags.NO_FAIL); // NO_FAIL (4)| Flag | Value | Effect |
|---|---|---|
DEFAULT | 0 | Allow create or update. |
CREATE_ONLY | 1 | Apply the operation only if the bin doesn’t already exist. Valid only on eight create-capable operations; see the caution below. |
UPDATE_ONLY | 2 | Apply the operation only to an existing bin. Valid on every modify operation. Against a missing bin, the operation is a silent no-op: it returns success without creating the bin. |
NO_FAIL | 4 | Return success and leave the bin at its prior value if the operation can’t be applied, instead of failing. String modify operations return no value in either case, so a suppressed operation and an applied one look the same in the response; read the bin to tell them apart. |
CREATE_ONLY rules
CREATE_ONLY is valid only on the eight operations that can create a missing bin: insert, overwrite, concat, append, prepend, pad_start, pad_end, and repeat.
The server rejects CREATE_ONLY with a ResultCode.PARAMETER_ERROR (server status AS_ERR_PARAMETER, code 4) in three cases:
- On any modify operation outside the eight listed above.
- Combined with
UPDATE_ONLYin the sameStringWriteFlagsvalue (both bits set on one operation, not two different operations in the sameOperate()call). - Combined with a nested context (
CTX) path.
NO_FAIL does not suppress any of the three. The server raises them while parsing the operation’s arguments, before the no-fail check runs.
StringPolicy is a per-operation argument, not client configuration: there’s no string-policy field on ClientPolicy. Construct a StringPolicy, or use StringPolicy.Default, and pass it to each call that needs non-default flags.
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 error either way. Read the bin back to confirm which operations applied rather than assuming success from the absence of an error.
Unlike the Go client, the C# client’s regex_replace honors the full StringPolicy. DEFAULT, UPDATE_ONLY, and NO_FAIL all take effect there. CREATE_ONLY is rejected, since regex_replace can’t create a bin. Pass regexFlags separately for regex behavior. See Regex and numeric-type flags.
Read operations
All read operations take the bin name as a StringOperation argument, or the source expression as the last StringExp argument. Both also take an optional CTX 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 ResultCode.BIN_TYPE_ERROR (server status AS_ERR_INCOMPATIBLE_TYPE, code 12; see Error codes):
try{ Record record = client.Operate(null, key, StringOperation.Strlen("email"));}catch (AerospikeException ae) when (ae.Result == ResultCode.BIN_TYPE_ERROR){ Console.Error.WriteLine($"'email' isn't a String bin: {ResultCode.GetResultString(ae.Result)}");}See Error handling - C# for the general AerospikeException/ae.Result pattern.
Index and length values count Unicode code points, not bytes. Most characters are one code point, but some emoji use multiple code points for one visible character, called a grapheme cluster. Characters outside the Basic Multilingual Plane (code points above U+FFFF, including many emoji and historic scripts) can also span multiple code points. Negative indexes count from the end of the string. RegexCompare uses International Components for Unicode (ICU) regex syntax.
Substring matching in find, contains, starts_with, and ends_with (and in replace/replace_all in Modify operations) treats canonically equivalent text as equal. Unicode canonical equivalence means two different code point sequences that represent the same character compare as identical, so a precomposed é (U+00E9) matches e followed by a combining acute accent (U+0301).
| Operation | C# builders | Returns | Description |
|---|---|---|---|
strlen | StringOperation.Strlen / StringExp.Strlen | integer | Code point count. |
byte_length | StringOperation.ByteLength / StringExp.ByteLength | integer | UTF-8 byte count. Differs from strlen for non-ASCII text. |
substr | StringOperation.Substr (two overloads) / StringExp.Substr (two overloads) | string | Substring from start to the end, or the half-open range [start, end). |
char_at | StringOperation.CharAt / StringExp.CharAt | string | The one-code-point string at index. |
find | StringOperation.Find (two overloads) / StringExp.Find (two overloads) | integer | Code point index of needle, or of a specific occurrence (1 = first, -1 = last). -1 if not found. |
contains | StringOperation.Contains / StringExp.Contains | boolean | Whether the bin contains needle. |
starts_with | StringOperation.StartsWith / StringExp.StartsWith | boolean | Whether the bin begins with prefix. |
ends_with | StringOperation.EndsWith / StringExp.EndsWith | boolean | Whether the bin ends with suffix. |
to_integer | StringOperation.ToInteger / StringExp.ToInteger | integer | Parses the string as a 64-bit integer. Fails if it doesn’t parse. See Type conversion. |
to_double | StringOperation.ToDouble / StringExp.ToDouble | float | Parses the string as a 64-bit float. Fails if it doesn’t parse. |
is_numeric | StringOperation.IsNumeric (two overloads) / StringExp.IsNumeric (two overloads) | boolean | Whether the bin’s spelling matches an optional StringNumericType (ANY, INT, or FLOAT). |
is_upper / is_lower | StringOperation.IsUpper, StringOperation.IsLower / StringExp.IsUpper, StringExp.IsLower | boolean | Whether every code point is an uppercase/lowercase letter. Digits, spaces, and punctuation aren’t cased letters, so any of them makes the result false. An empty string returns true. |
to_blob | StringOperation.ToBlob / StringExp.ToBlob | blob | The UTF-8 bytes of the string, as a Blob. |
split | StringOperation.Split (two overloads) / StringExp.Split (two overloads) | list | Splits by Unicode code point, or by separator (a singleton list if separator isn’t found). |
b64_decode | StringOperation.B64Decode / StringExp.B64Decode | blob | Decodes the bin as base64 text into a Blob. Fails if it isn’t valid base64. |
regex_compare | StringOperation.RegexCompare (two overloads) / StringExp.RegexCompare (two overloads) | boolean | Matches an ICU regex pattern against the bin, optionally with StringRegexFlags. |
Seven read operations (contains, starts_with, ends_with, is_numeric, is_upper, is_lower, regex_compare) return a native boolean, not an integer 0/1. See Reading operate results.
Modify operations
Modify operations write a transformed value back to the bin (StringOperation) or return it as an expression value (StringExp, which does not mutate the underlying bin). Every modify operation accepts DEFAULT, UPDATE_ONLY, or NO_FAIL; the eight operations in this table’s first eight rows also accept CREATE_ONLY.
| Operation | C# builders | Description |
|---|---|---|
insert | StringOperation.Insert / StringExp.Insert | Splices value in at code point index. |
overwrite | StringOperation.Overwrite / StringExp.Overwrite | Overwrites code points starting at index with value. |
concat | StringOperation.Concat (two overloads) / StringExp.Concat | Appends one string, or each element of a list of strings, in order. |
append | StringOperation.Append / StringExp.Append | Appends value. Unicode-aware, including double-byte character sets (DBCS), unlike the legacy Operation.Append. |
prepend | StringOperation.Prepend / StringExp.Prepend | Prepends value. Unicode-aware, including DBCS, unlike the legacy Operation.Prepend. |
pad_start | StringOperation.PadStart / StringExp.PadStart | Left-pads with padString up to targetLength code points. No-op if already at or above the target. |
pad_end | StringOperation.PadEnd / StringExp.PadEnd | Right-pads with padString up to targetLength code points. |
repeat | StringOperation.Repeat / StringExp.Repeat | Repeats the bin count times. |
snip | StringOperation.Snip (two overloads) / StringExp.Snip (two overloads) | Removes from start to the end, or the half-open range [start, end). See the caution above about the start-only overload. |
replace | StringOperation.Replace / StringExp.Replace | Replaces the first occurrence of needle with replacement. |
replace_all | StringOperation.ReplaceAll / StringExp.ReplaceAll | Replaces every occurrence of needle with replacement. |
upper / lower | StringOperation.Upper, StringOperation.Lower / StringExp.Upper, StringExp.Lower | Uppercases or lowercases the bin. |
case_fold | StringOperation.CaseFold / StringExp.CaseFold | Maps characters to a common case for locale-independent, case-insensitive comparison keys. |
normalize_nfc | StringOperation.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 | StringOperation.Trim, StringOperation.TrimStart, StringOperation.TrimEnd / StringExp.Trim, StringExp.TrimStart, StringExp.TrimEnd | Removes Unicode whitespace from both ends, the start, or the end. |
regex_replace | StringOperation.RegexReplace / StringExp.RegexReplace | Replaces the first regex match, or every match when StringRegexFlags.GLOBAL is set. Honors the full StringPolicy; see String write policy. |
Type conversion
StringOperation.ToString/StringExp.ToString converts an Integer, Float, Boolean, String, or Blob bin to its string representation. It fails with ResultCode.BIN_TYPE_ERROR (see Error codes) for any other bin type, and with ResultCode.OP_NOT_APPLICABLE (server status AS_ERR_OP_NOT_APPLICABLE, code 26) if a Blob bin’s bytes aren’t valid UTF-8.
Record record = client.Operate(null, key, StringOperation.ToString("age"));Console.WriteLine(record.GetString("age"));StringOperation.ToString is the only operation that does not accept a CTX. 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 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.
to_integer/to_double parse failures and to_string’s invalid-UTF-8 case both surface as ResultCode.OP_NOT_APPLICABLE, per the String operations error codes.
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 StringOperation.IsNumeric(bin, StringNumericType.FLOAT) against "5" returns 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 bool, not an integer 0/1:
Record record = client.Operate(null, key, StringOperation.Contains("email", "@"));bool hasAt = record.GetBool("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 IList, using record.GetList(binName), with one entry per operation. See Returning from operate() in Bin operations for the general grouping rule.
String operations always respond
Unlike some collection data type (CDT) operations, a String read or modify operation always contributes an entry to the grouped result list, with no policy change needed. Adding any String read or modify operation to an Operate() call makes the whole call behave as if WritePolicy.respondAllOps were set, whether or not you set it yourself.
Every String operation in that call gets a predictable, stable index, including a modify operation mixed with reads on the same bin. This applies to the entire Operate() call, not only its String operations: any other operation type in the same call that wouldn’t normally return a result on its own now also returns one, since the flag is set for the whole request.
A modify operation returns no value either way, whether it applied or a NO_FAIL flag suppressed it. Its list entry carries no value, so read the bin back if you need to confirm which modify operations actually applied.
Record record = client.Operate(null, key, StringOperation.Trim(StringPolicy.Default, "email"), // modify: entry 0 StringOperation.Strlen("email"), // read: entry 1 StringOperation.Substr("email", 0, 5)); // read: entry 2
IList results = record.GetList("email");long length = (long)results[1];string head = (string)results[2];A single string operation on a bin, with nothing else targeting that bin, returns its value directly, with no IList wrapper.
Nested strings
StringOperation builders take an optional trailing CTX (or an array of them) 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 ResultCode.BIN_TYPE_ERROR. An invalid path, such as an out-of-bounds list index or a missing map key, also fails. See Context for operations on nested elements for general CTX error behavior.
// 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 don’t 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.Strlen condition, then uses it two ways: as a read filter, and as a projected read value.
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 code pointsPolicy readPolicy = new Policy();readPolicy.filterExp = Exp.Build(isLong);Record record = client.Get(readPolicy, key);
// As a projection: always fetch the record, with the condition's result in a computed binrecord = client.Operate(null, key, ExpOperation.Read("isLong", Exp.Build(isLong), ExpReadFlags.DEFAULT));bool bioIsLong = record.GetBool("isLong");A filtered-out record returns null by default. See the failOnFilteredOut note on the Expressions page to raise an exception instead.
StringOperation.ToString/StringExp.ToString never accepts a CTX, on either surface. See Type conversion.
Version requirements
String operations require Aerospike Database 8.2.0 or later on every node, and Aerospike C# client 8.5.0 or later. A server 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 Database 8.2.0 or later. Run dotnet list package in your project directory and check the Aerospike.Client row to confirm the installed client version.
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, including DBCS. The legacy pair does a raw byte concatenation and doesn’t supportStringPolicyorCTX. - Both legacy operations also accept Blob bins, which the string package can’t target. For a Blob bin, keep using
Operation.Append/Operation.Prepend. There’s no string-module replacement, and the legacy behavior is unchanged and fully supported for that case. - The legacy
Exp.RegexCompare(POSIX regex) is deprecated in favor ofStringExp.RegexCompare, which is Unicode-aware (ICU regex).
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 - C#
- Expressions - C#: building and using expressions, filter policies, and
ExpOperation.Read/ExpOperation.Write - Error handling - C#: the
AerospikeException/ae.Resultpattern - Error codes: full server status code list, including String-operation-specific entries
- API reference (C#)