Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
bca05b9
Compile with -Werror for linux and macos.
juliannguyen4 Jun 1, 2026
c80b412
Update documentation to reflect latest changes in PRD. TODO - need ad…
juliannguyen4 Jul 14, 2026
4b0e04a
Refactor string op tests by combining all test cases that operate on …
juliannguyen4 Jul 14, 2026
352cef8
Add test_string_ops_on_nonexistent_bin_creates_it for additive string…
juliannguyen4 Jul 14, 2026
1f91d0d
Refactor string op tests by combining all test cases that operate on …
juliannguyen4 Jul 14, 2026
221e0a4
Address incorrect tests for pad_start and pad_end on nonexistent bin.
juliannguyen4 Jul 14, 2026
061a174
Since all tests in this function expect NEEDLE as the output, don't c…
juliannguyen4 Jul 14, 2026
4416fb5
Add negative tests that check the other string write ops don't create…
juliannguyen4 Jul 15, 2026
96c0595
Address failing negative tests where operations.read() will return a …
juliannguyen4 Jul 15, 2026
d1a66a1
Merge branch 'CLIENT-5057-refactor-string-ops-tests' into CLIENT-5057…
juliannguyen4 Jul 15, 2026
0dfc023
Document how each string op behaves with regards to a nonexistent bin…
juliannguyen4 Jul 15, 2026
028688a
Address to_string expression and operation not being implemented... T…
juliannguyen4 Jul 15, 2026
b08d986
Fully implement to_string() expression and operation. TODO - to_strin…
juliannguyen4 Jul 15, 2026
69c45b1
Merge remote-tracking branch 'origin/dev' into CLIENT-4869-fix-macos-…
juliannguyen4 Jul 15, 2026
dac0e4f
Merge branch 'CLIENT-4869-fix-macos-failing-to-compile-with-Werror-fl…
juliannguyen4 Jul 15, 2026
1a14d52
To make simpler, just have ToString expression reuse aerospike._OP_ST…
juliannguyen4 Jul 15, 2026
3f97c8d
The problem was passing in the bin name as a string to string express…
juliannguyen4 Jul 15, 2026
e507e82
Merge remote-tracking branch 'origin/dev' into CLIENT-5057-document-s…
juliannguyen4 Jul 16, 2026
9012927
Revert changes that have nothing to do with append or prepend.
juliannguyen4 Jul 16, 2026
09f48eb
Clear up that passing the bin name as a Python str will expect the bi…
juliannguyen4 Jul 16, 2026
0bbf0b5
Address test issue.
juliannguyen4 Jul 16, 2026
8d9e20a
Remove unused helper variable for string ops tests.
juliannguyen4 Jul 16, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 18 additions & 17 deletions aerospike_helpers/expressions/string.py
Original file line number Diff line number Diff line change
Expand Up @@ -401,6 +401,24 @@ def __init__(self, pattern: str, bin: "TypeBinName", regex_flags: RegexFlags = R
self._children = (_convert_bin_name_to_expr(bin),)


class ToString(_BaseExpr):
_op = aerospike._OP_STRING_TO_STRING

def __init__(self, bin: "TypeBinName"):
"""
Args:

bin: A bin expression to apply this function to.
If this argument is a string, the bin must contain a string.

Returns:

The string in the bin with the value converted to a string.

"""
self._children = (_convert_bin_name_to_expr(bin),)


class _WriteOp(_BaseExpr):
def __init__(self, policy: StringPolicy):
self._fixed = {
Expand Down Expand Up @@ -823,23 +841,6 @@ def __init__(
self._children = (_convert_bin_name_to_expr(bin),)


class ToString(_WriteOp):
_op = aerospike._AS_EXP_CODE_CALL

def __init__(self, bin: "TypeBinName"):
"""
Args:

bin: A bin expression to apply this function to.

Returns:

The string in the bin with the value converted to a string.

"""
self._children = (_convert_bin_name_to_expr(bin),)


__exp_class_to_op_func = {
StrLen: str_ops.strlen,
SubStr: str_ops.substr,
Expand Down
36 changes: 19 additions & 17 deletions aerospike_helpers/operations/string_operations.py
Original file line number Diff line number Diff line change
Expand Up @@ -397,6 +397,25 @@ def regex_compare(bin_name: str, pattern: str, regex_flags: RegexFlags = RegexFl
}


def to_string(bin_name: str):
"""
Create ``to_string`` operation that converts an integer, double, string, or blob
bin to its string representation.

Raises :exc:`~aerospike.exception.BinIncompatibleType` for
any other bin type. This top-level operation does not accept ctx and does not
send a msgpack payload.

Args:

bin_name: name of string bin.
"""
return {
"op": aerospike._OP_STRING_TO_STRING,
"bin": bin_name,
}


def insert(bin_name: str, index: int, value: str, policy: StringPolicy | None = None, ctx: TypeCTX = None):
"""
Create string ``insert`` operation that splices value into the bin at codepoint
Expand Down Expand Up @@ -818,20 +837,3 @@ def regex_replace(
"policy": policy,
"ctx": ctx
}


def to_string(bin_name: str):
"""
Create ``to_string`` operation that converts an integer, double, string, or blob
bin to its string representation. Raises :exc:`~aerospike.exception.BinIncompatibleType` for
any other bin type. This top-level operation does not accept ctx and does not
send a msgpack payload.

Args:

bin_name: name of string bin.
"""
return {
"op": aerospike._OP_STRING_TO_STRING,
"bin": bin_name,
}
3 changes: 2 additions & 1 deletion src/include/policy.h
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,8 @@ enum Aerospike_send_bool_as_values {
X(STRING_REPEAT), \
X(STRING_REGEX_REPLACE), \
X(STRING_APPEND), \
X(STRING_PREPEND),
X(STRING_PREPEND), \
X(STRING_TO_STRING),
// clang-format on

enum {
Expand Down
2 changes: 1 addition & 1 deletion src/main/client/operate.c
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,7 @@ static inline bool use_operate_conversion_helper(int op)
op == OP_LIST_REMOVE_BY_VALUE_RANGE || op == OP_LIST_SET_ORDER ||
op == OP_LIST_SORT || op == OP_LIST_REMOVE_BY_VALUE_RANK_RANGE_REL ||
op == OP_LIST_GET_BY_VALUE_RANK_RANGE_REL || op == OP_LIST_CREATE ||
(op >= OP_STRING_STRLEN && op <= OP_STRING_PREPEND) ||
(op >= OP_STRING_STRLEN && op <= OP_STRING_TO_STRING) ||
(op == OP_MAP_REMOVE_BY_KEY_INDEX_RANGE_REL ||
op == OP_MAP_REMOVE_BY_VALUE_RANK_RANGE_REL ||
op == OP_MAP_GET_BY_VALUE_RANK_RANGE_REL ||
Expand Down
4 changes: 3 additions & 1 deletion src/main/client/operate_helper.c
Original file line number Diff line number Diff line change
Expand Up @@ -766,7 +766,9 @@ as_status as_operations_add_from_pyobject(AerospikeClient *self, as_error *err,
success = as_operations_string_prepend(ops, bin, ctx_ref, &str_policy,
str_attr_value1);
break;

case OP_STRING_TO_STRING:
success = as_operations_to_string(ops, bin);
break;
case OP_MAP_REMOVE_BY_VALUE_RANK_RANGE_REL: {
if (range_specified) {
success = as_operations_map_remove_by_value_rel_rank_range(
Expand Down
4 changes: 4 additions & 0 deletions src/main/convert_expressions.c
Original file line number Diff line number Diff line change
Expand Up @@ -473,6 +473,7 @@ static as_status get_expr_size(int *size_to_alloc, int *intermediate_exprs_size,
[OP_STRING_B64_DECODE] = EXP_SZ(as_exp_string_b64_decode(NIL)),
[OP_STRING_REGEX_COMPARE] =
EXP_SZ(as_exp_string_regex_compare_flags("", 0, NIL)),
[OP_STRING_TO_STRING] = EXP_SZ(as_exp_to_string(NIL)),
[OP_STRING_INSERT] = EXP_SZ(as_exp_string_insert(NULL, 0, "", NIL)),
[OP_STRING_OVERWRITE] =
EXP_SZ(as_exp_string_overwrite(NULL, 0, "", NIL)),
Expand Down Expand Up @@ -1908,6 +1909,9 @@ add_expr_macros(AerospikeClient *self, as_static_pool *static_pool,
case OP_STRING_B64_DECODE:
APPEND_ARRAY(1, as_exp_string_b64_decode(NIL));
break;
case OP_STRING_TO_STRING:
APPEND_ARRAY(1, as_exp_to_string(NIL));
break;
case OP_STRING_REGEX_REPLACE:
case OP_STRING_REGEX_COMPARE: {
char *pattern = NULL;
Expand Down
10 changes: 7 additions & 3 deletions test/new_tests/string_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@

KEY = KEYS[0]

NON_STR_BIN_NAME = "nonstr"
NON_STR_BIN_NAME = INT_BIN_NAME = "nonstr"
STR_BIN_NAME = "str"
DOUBLE_BIN_NAME = "double"
BLOB_BIN_NAME = "blob"
UPPERCASE_STR_BIN_NAME = "uppercase_str"
NESTED_STR_BIN_NAME = "nested_str"
STR_WITH_INT_BIN_NAME = "str_with_int"
Expand Down Expand Up @@ -37,7 +39,7 @@

BINS = {
STR_BIN_NAME: EXAMPLE_STR,
NON_STR_BIN_NAME: 1,
INT_BIN_NAME: 1,
UPPERCASE_STR_BIN_NAME: UPPERCASE_STR,
NESTED_STR_BIN_NAME: [EXAMPLE_STR],
STR_WITH_INT_BIN_NAME: STRING_WITH_INT,
Expand All @@ -46,7 +48,9 @@
BASE64_ENCODED_BIN_NAME: BASE64_ENCODED_STR,
SURROUNDING_WHITESPACE_BIN_NAME: EXAMPLE_STR_WITH_SURROUNDING_WHITESPACE,
MULTILINE_STR_BIN_NAME: MULTILINE_STR,
MULTILINE_STR_WITH_CR_BIN_NAME: MULTILINE_STR_WITH_CR
MULTILINE_STR_WITH_CR_BIN_NAME: MULTILINE_STR_WITH_CR,
DOUBLE_BIN_NAME: 1.0,
BLOB_BIN_NAME: b'123'
}

START_IDX = 1
Expand Down
20 changes: 18 additions & 2 deletions test/new_tests/test_expressions_string.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import pytest
import base64

from aerospike_helpers.expressions import string as str_expr
from aerospike_helpers.expressions import string as str_expr, IntBin, FloatBin, BlobBin
from aerospike_helpers.operations import expression_operations as expr_ops
from aerospike_helpers.operations import operations
from aerospike_helpers.string_helpers import NumericType, RegexFlags
Expand Down Expand Up @@ -67,7 +67,23 @@ def setup(self, request, as_connection, expect_earlier_than_server_version_to_fa
(
str_expr.RegexCompare(pattern="π", regex_flags=RegexFlags.DEFAULT, bin=MULTIBYTE_CODEPOINT_BIN_NAME),
False
)
),
(
str_expr.ToString(bin=IntBin(INT_BIN_NAME)),
str(BINS[INT_BIN_NAME])
),
(
str_expr.ToString(bin=FloatBin(DOUBLE_BIN_NAME)),
str(BINS[INT_BIN_NAME])
),
(
str_expr.ToString(bin=STR_BIN_NAME),
str(BINS[STR_BIN_NAME])
),
(
str_expr.ToString(bin=BlobBin(BLOB_BIN_NAME)),
bytes.decode(BINS[BLOB_BIN_NAME])
),
]
)
@expect_server_version_earlier_than_8_1_3_to_fail
Expand Down
20 changes: 20 additions & 0 deletions test/new_tests/test_string_operations.py
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,26 @@ def test_regex_compare(self, pattern: str, expected_result: bool):

assert bins[MULTIBYTE_CODEPOINT_BIN_NAME] is expected_result

@expect_server_version_earlier_than_8_1_3_to_fail
@pytest.mark.parametrize(
"bin_name, expected_result",
[
(INT_BIN_NAME, str(BINS[INT_BIN_NAME])),
(DOUBLE_BIN_NAME, str(BINS[INT_BIN_NAME])),
(STR_BIN_NAME, BINS[STR_BIN_NAME]),
(BLOB_BIN_NAME, bytes.decode(BINS[BLOB_BIN_NAME]))
]
)
def test_to_string(self, bin_name: str, expected_result: str):
ops = [
str_ops.to_string(bin_name=bin_name)
]

with self.expected_context_for_pos_tests:
_, _, bins = self.as_connection.operate(KEY, ops)

assert bins[bin_name] == expected_result

# Write operations

def add_read_op(self, ops, bin_name):
Expand Down
Loading