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. 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 configuration
Host config = new Host("127.0.0.1", 3000);
// Establishes a connection to the server
AerospikeClient 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, write
Record 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 trip
client.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:

SurfaceNamingUsed withArgument order
OperationStringOperation.*client.Operate()Bin name first: StringOperation.Strlen(binName, ctx...)
ExpressionStringExp.*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 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 StringWriteFlags value:

StringPolicy policy = StringPolicy.Default; // DEFAULT (0)
StringPolicy custom = new StringPolicy(StringWriteFlags.NO_FAIL); // NO_FAIL (4)
FlagValueEffect
DEFAULT0Allow create or update.
CREATE_ONLY1Apply the operation only if the bin doesn’t already exist. Valid only on eight create-capable operations; see the caution below.
UPDATE_ONLY2Apply 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_FAIL4Return 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_ONLY in the same StringWriteFlags value (both bits set on one operation, not two different operations in the same Operate() 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).

OperationC# buildersReturnsDescription
strlenStringOperation.Strlen / StringExp.StrlenintegerCode point count.
byte_lengthStringOperation.ByteLength / StringExp.ByteLengthintegerUTF-8 byte count. Differs from strlen for non-ASCII text.
substrStringOperation.Substr (two overloads) / StringExp.Substr (two overloads)stringSubstring from start to the end, or the half-open range [start, end).
char_atStringOperation.CharAt / StringExp.CharAtstringThe one-code-point string at index.
findStringOperation.Find (two overloads) / StringExp.Find (two overloads)integerCode point index of needle, or of a specific occurrence (1 = first, -1 = last). -1 if not found.
containsStringOperation.Contains / StringExp.ContainsbooleanWhether the bin contains needle.
starts_withStringOperation.StartsWith / StringExp.StartsWithbooleanWhether the bin begins with prefix.
ends_withStringOperation.EndsWith / StringExp.EndsWithbooleanWhether the bin ends with suffix.
to_integerStringOperation.ToInteger / StringExp.ToIntegerintegerParses the string as a 64-bit integer. Fails if it doesn’t parse. See Type conversion.
to_doubleStringOperation.ToDouble / StringExp.ToDoublefloatParses the string as a 64-bit float. Fails if it doesn’t parse.
is_numericStringOperation.IsNumeric (two overloads) / StringExp.IsNumeric (two overloads)booleanWhether the bin’s spelling matches an optional StringNumericType (ANY, INT, or FLOAT).
is_upper / is_lowerStringOperation.IsUpper, StringOperation.IsLower / StringExp.IsUpper, StringExp.IsLowerbooleanWhether 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_blobStringOperation.ToBlob / StringExp.ToBlobblobThe UTF-8 bytes of the string, as a Blob.
splitStringOperation.Split (two overloads) / StringExp.Split (two overloads)listSplits by Unicode code point, or by separator (a singleton list if separator isn’t found).
b64_decodeStringOperation.B64Decode / StringExp.B64DecodeblobDecodes the bin as base64 text into a Blob. Fails if it isn’t valid base64.
regex_compareStringOperation.RegexCompare (two overloads) / StringExp.RegexCompare (two overloads)booleanMatches 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.

OperationC# buildersDescription
insertStringOperation.Insert / StringExp.InsertSplices value in at code point index.
overwriteStringOperation.Overwrite / StringExp.OverwriteOverwrites code points starting at index with value.
concatStringOperation.Concat (two overloads) / StringExp.ConcatAppends one string, or each element of a list of strings, in order.
appendStringOperation.Append / StringExp.AppendAppends value. Unicode-aware, including double-byte character sets (DBCS), unlike the legacy Operation.Append.
prependStringOperation.Prepend / StringExp.PrependPrepends value. Unicode-aware, including DBCS, unlike the legacy Operation.Prepend.
pad_startStringOperation.PadStart / StringExp.PadStartLeft-pads with padString up to targetLength code points. No-op if already at or above the target.
pad_endStringOperation.PadEnd / StringExp.PadEndRight-pads with padString up to targetLength code points.
repeatStringOperation.Repeat / StringExp.RepeatRepeats the bin count times.
snipStringOperation.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.
replaceStringOperation.Replace / StringExp.ReplaceReplaces the first occurrence of needle with replacement.
replace_allStringOperation.ReplaceAll / StringExp.ReplaceAllReplaces every occurrence of needle with replacement.
upper / lowerStringOperation.Upper, StringOperation.Lower / StringExp.Upper, StringExp.LowerUppercases or lowercases the bin.
case_foldStringOperation.CaseFold / StringExp.CaseFoldMaps characters to a common case for locale-independent, case-insensitive comparison keys.
normalize_nfcStringOperation.NormalizeNFC / StringExp.NormalizeNFCNormalizes the bin to Unicode Normalization Form C (NFC), the canonical composed form. Already-normalized strings are unchanged.
trim / trim_start / trim_endStringOperation.Trim, StringOperation.TrimStart, StringOperation.TrimEnd / StringExp.Trim, StringExp.TrimStart, StringExp.TrimEndRemoves Unicode whitespace from both ends, the start, or the end.
regex_replaceStringOperation.RegexReplace / StringExp.RegexReplaceReplaces 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:

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 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 false

Multiple 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 points
Policy 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 bin
record = 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) and Operation.Prepend(bin) are deprecated for String bins only, in favor of StringOperation.Append/StringOperation.Prepend, which are Unicode-aware, including DBCS. The legacy pair does a raw byte concatenation and doesn’t support StringPolicy or CTX.
  • 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 of StringExp.RegexCompare, which is Unicode-aware (ICU regex).

Next steps