Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
7 changes: 4 additions & 3 deletions aerospike-stubs/aerospike.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -310,9 +310,10 @@ TXN_STATE_COMMITTED: Literal[2]
TXN_STATE_ABORTED: Literal[3]

CDT_SELECT_MATCHING_TREE: Literal[0]
CDT_SELECT_VALUES: Literal[1]
CDT_SELECT_MAP_KEY_VALUES: Literal[1]
CDT_SELECT_MAP_KEYS: Literal[2]
CDT_SELECT_MAP_VALUE: Literal[1]
CDT_SELECT_LIST_VALUE: Literal[1]
CDT_SELECT_MAP_KEY: Literal[2]
CDT_SELECT_MAP_KEY_VALUES: Literal[3]
CDT_SELECT_NO_FAIL: Literal[0x10]

CDT_MODIFY_DEFAULT: Literal[0]
Expand Down
21 changes: 21 additions & 0 deletions aerospike_helpers/expressions/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -1112,6 +1112,27 @@ class LoopVarInt(LoopVar):
_op = aerospike._AS_EXP_LOOPVAR_INT


class LoopVarBlob(LoopVar):
_op = aerospike._AS_EXP_LOOPVAR_BLOB


class LoopVarBool(LoopVar):
_op = aerospike._AS_EXP_LOOPVAR_BOOL


class ResultRemove(_BaseExpr):
"""
Indicates entry deletion for modify_by_path.
"""
_op = aerospike._AS_EXP_CODE_RESULT_REMOVE

def __init__(self):
"""
:return: (result_remove)
"""
pass


class SelectByPath(_BaseExpr):
"""
Constructs a select by path operation. This is used to retrieve a number of
Expand Down
17 changes: 10 additions & 7 deletions doc/aerospike.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1798,29 +1798,32 @@ Transaction State

.. _cdt_select_flags:

CDT Select Flags
----------------
Path Expression Flags
---------------------

.. data:: CDT_SELECT_MATCHING_TREE

Return a tree from the root (bin) level to the bottom of the tree, with only non-filtered out nodes.

.. data:: CDT_SELECT_VALUES
.. data:: CDT_SELECT_LIST_VALUE
.. data:: CDT_SELECT_MAP_VALUE

Return the list of the values of the nodes finally selected by the context.

.. data:: CDT_SELECT_MAP_KEY_VALUES
.. data:: CDT_SELECT_MAP_KEYS

Return a list of key-value pairs.
Return the list of map keys of the nodes finally selected by the context.

.. data:: CDT_SELECT_MAP_KEYS
.. data:: CDT_SELECT_MAP_KEY_VALUES

For final selected nodes which are elements of maps, return the appropriate map key.
Returns the list of map (key, value) pairs of the nodes finally selected
by the context.

.. data:: CDT_SELECT_NO_FAIL

If the expression in the context hits an invalid type (e.g selects as an integer when the value is a string),
do not fail the operation; just ignore those elements.
do not fail the operation; just ignore those elements. Interpret UNKNOWN as false instead.

.. _cdt_modify_flags:

Expand Down
2 changes: 2 additions & 0 deletions src/include/policy.h
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,8 @@ enum {
_AS_EXP_LOOPVAR_LIST,
_AS_EXP_LOOPVAR_MAP,
_AS_EXP_LOOPVAR_STR,
_AS_EXP_LOOPVAR_BLOB,
_AS_EXP_LOOPVAR_BOOL,
_AS_EXP_CODE_CALL_SELECT,
_AS_EXP_CODE_CALL_APPLY,
};
Expand Down
13 changes: 9 additions & 4 deletions src/main/aerospike.c
Original file line number Diff line number Diff line change
Expand Up @@ -562,10 +562,12 @@ static struct module_constant_name_to_value module_constants[] = {
EXPOSE_AS_MACRO_WITHOUT_AS_PREFIX_AS_PUBLIC_FIELD(EXP_LOOPVAR_INDEX),

EXPOSE_AS_MACRO_WITHOUT_AS_PREFIX_AS_PUBLIC_FIELD(CDT_SELECT_MATCHING_TREE),
EXPOSE_AS_MACRO_WITHOUT_AS_PREFIX_AS_PUBLIC_FIELD(CDT_SELECT_VALUES),
EXPOSE_AS_MACRO_WITHOUT_AS_PREFIX_AS_PUBLIC_FIELD(
CDT_SELECT_MAP_KEY_VALUES),
EXPOSE_AS_MACRO_WITHOUT_AS_PREFIX_AS_PUBLIC_FIELD(CDT_SELECT_MAP_KEYS),
// TODO: remove since this isn't a C client enum value
{"CDT_SELECT_VALUES", .value.integer = AS_CDT_SELECT_MAP_VALUE},
EXPOSE_AS_MACRO_WITHOUT_AS_PREFIX_AS_PUBLIC_FIELD(CDT_SELECT_MAP_VALUE),
EXPOSE_AS_MACRO_WITHOUT_AS_PREFIX_AS_PUBLIC_FIELD(CDT_SELECT_LIST_VALUE),
EXPOSE_AS_MACRO_WITHOUT_AS_PREFIX_AS_PUBLIC_FIELD(CDT_SELECT_MAP_KEY),
EXPOSE_AS_MACRO_WITHOUT_AS_PREFIX_AS_PUBLIC_FIELD(CDT_SELECT_MAP_KEY_VALUE),
EXPOSE_AS_MACRO_WITHOUT_AS_PREFIX_AS_PUBLIC_FIELD(CDT_SELECT_NO_FAIL),

EXPOSE_AS_MACRO_WITHOUT_AS_PREFIX_AS_PUBLIC_FIELD(CDT_MODIFY_NO_FAIL),
Expand All @@ -579,11 +581,14 @@ static struct module_constant_name_to_value module_constants[] = {
EXPOSE_MACRO(_AS_EXP_LOOPVAR_LIST),
EXPOSE_MACRO(_AS_EXP_LOOPVAR_MAP),
EXPOSE_MACRO(_AS_EXP_LOOPVAR_STR),
EXPOSE_MACRO(_AS_EXP_LOOPVAR_BLOB),
EXPOSE_MACRO(_AS_EXP_LOOPVAR_BOOL),

// C client uses the same expression code for these two expressions
// so we define unique ones in the Python client code
EXPOSE_MACRO(_AS_EXP_CODE_CALL_SELECT),
EXPOSE_MACRO(_AS_EXP_CODE_CALL_APPLY),
EXPOSE_MACRO(_AS_EXP_CODE_RESULT_REMOVE),

EXPOSE_STRING_MACRO_FOR_AEROSPIKE_HELPERS(_CDT_FLAGS_KEY),
EXPOSE_STRING_MACRO_FOR_AEROSPIKE_HELPERS(_CDT_APPLY_MOD_EXP_KEY),
Expand Down
14 changes: 14 additions & 0 deletions src/main/convert_expressions.c
Original file line number Diff line number Diff line change
Expand Up @@ -210,13 +210,16 @@ static as_status get_expr_size(int *size_to_alloc, int *intermediate_exprs_size,
EXP_SZ(as_exp_select_by_path(NULL, 0, 0, NIL)),
[_AS_EXP_CODE_CALL_APPLY] =
EXP_SZ(as_exp_modify_by_path(NULL, 0, NULL, 0, NIL)),
[_AS_EXP_CODE_RESULT_REMOVE] = EXP_SZ(as_exp_result_remove()),
[BIN] = EXP_SZ(as_exp_bin_int(0)),
[_AS_EXP_CODE_AS_VAL] = EXP_SZ(as_exp_val(NULL)),
[_AS_EXP_LOOPVAR_FLOAT] = EXP_SZ(as_exp_loopvar_float(0)),
[_AS_EXP_LOOPVAR_INT] = EXP_SZ(as_exp_loopvar_int(0)),
[_AS_EXP_LOOPVAR_LIST] = EXP_SZ(as_exp_loopvar_list(0)),
[_AS_EXP_LOOPVAR_MAP] = EXP_SZ(as_exp_loopvar_map(0)),
[_AS_EXP_LOOPVAR_STR] = EXP_SZ(as_exp_loopvar_str(0)),
[_AS_EXP_LOOPVAR_BOOL] = EXP_SZ(as_exp_loopvar_bool(0)),
[_AS_EXP_LOOPVAR_BLOB] = EXP_SZ(as_exp_loopvar_blob(0)),
[VAL] = EXP_SZ(as_exp_val(
NULL)), // NOTE if I don't count vals I don't need to subtract from other ops // MUST count these for expressions with var args.
[EQ] = EXP_SZ(
Expand Down Expand Up @@ -652,6 +655,8 @@ add_expr_macros(AerospikeClient *self, as_static_pool *static_pool,
case _AS_EXP_LOOPVAR_LIST:
case _AS_EXP_LOOPVAR_MAP:
case _AS_EXP_LOOPVAR_STR:
case _AS_EXP_LOOPVAR_BOOL:
case _AS_EXP_LOOPVAR_BLOB:
if (get_int64_t(err, AS_PY_VAL_KEY, temp_expr->pydict, &lval1) !=
AEROSPIKE_OK) {
return err->code;
Expand All @@ -673,6 +678,12 @@ add_expr_macros(AerospikeClient *self, as_static_pool *static_pool,
case _AS_EXP_LOOPVAR_FLOAT:
APPEND_ARRAY(0, as_exp_loopvar_float(lval1));
break;
case _AS_EXP_LOOPVAR_BLOB:
APPEND_ARRAY(0, as_exp_loopvar_blob(lval1));
break;
case _AS_EXP_LOOPVAR_BOOL:
APPEND_ARRAY(0, as_exp_loopvar_bool(lval1));
break;
}

break;
Expand Down Expand Up @@ -1683,6 +1694,9 @@ add_expr_macros(AerospikeClient *self, as_static_pool *static_pool,
lval2, NIL));
}
break;
case _AS_EXP_CODE_RESULT_REMOVE:
APPEND_ARRAY(0, as_exp_result_remove());
break;
default:
return as_error_update(err, AEROSPIKE_ERR_PARAM,
"Unrecognised expression op type.");
Expand Down
54 changes: 47 additions & 7 deletions test/new_tests/test_cdt_select.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import aerospike
from aerospike_helpers.operations import operations
from aerospike_helpers.expressions.resources import ResultType
from aerospike_helpers.expressions.base import GE, Eq, LoopVarStr, LoopVarFloat, LoopVarInt, LoopVarMap, LoopVarList, ModifyByPath, SelectByPath, MapBin
from aerospike_helpers.expressions.base import GE, Eq, LoopVarStr, LoopVarFloat, LoopVarInt, LoopVarMap, LoopVarList, ModifyByPath, SelectByPath, MapBin, LoopVarBool, LoopVarBlob, ResultRemove
from aerospike_helpers.expressions.map import MapGetByKey
from aerospike_helpers.expressions.list import ListSize
from aerospike_helpers.expressions.arithmetic import Sub
Expand Down Expand Up @@ -36,7 +36,9 @@ class TestCDTSelectOperations:
"ab": {
"bb": 12
},
"b": 2
"b": 2,
"c": True,
"d": bytearray(b'123')
},
LIST_BIN_NAME: [
{
Expand Down Expand Up @@ -194,7 +196,8 @@ def test_cdt_select_with_filter(self):
pytest.param(
GE(LoopVarInt(aerospike.EXP_LOOPVAR_VALUE), 2),
# Should filter out 1
[2]
[2],
id="LoopVarInt"
),
# At the first level below root, only return maps that have a key "bb" with value >= 10
pytest.param(
Expand All @@ -208,11 +211,22 @@ def test_cdt_select_with_filter(self):
),
expr1=10
),
[RECORD_BINS[MAP_BIN_NAME]["ab"]]
)
[RECORD_BINS[MAP_BIN_NAME]["ab"]],
id="LoopVarMap"
),
pytest.param(
Eq(LoopVarBool(aerospike.EXP_LOOPVAR_VALUE), True),
[True],
id="LoopVarBool"
),
pytest.param(
Eq(LoopVarBlob(aerospike.EXP_LOOPVAR_VALUE), b'123'),
[bytearray(b'123')],
id="LoopVarBlob"
),
]
)
def test_exp_loopvar_int_and_map(self, filter_expr, expected_bin_value):
def test_exp_loopvar_types(self, filter_expr, expected_bin_value):
ops = [
operations.select_by_path(
bin_name=self.MAP_BIN_NAME,
Expand Down Expand Up @@ -314,7 +328,7 @@ def test_cdt_select_flag_map_keys(self):
cdt_ctx.cdt_ctx_all_children(),
cdt_ctx.cdt_ctx_all_children()
],
flags=aerospike.CDT_SELECT_MAP_KEYS
flags=aerospike.CDT_SELECT_MAP_KEY
)
]

Expand Down Expand Up @@ -427,3 +441,29 @@ def test_loopvar_id_list_index(self):
_, _, bins = self.as_connection.operate(self.key, ops)
# Return the same list, but with all list elements except at index 0 removed
assert bins == {self.LIST_BIN_NAME: [self.RECORD_BINS[self.LIST_BIN_NAME][0]]}

def test_expr_result_remove(self):
ops = [
operations.modify_by_path(
bin_name=self.MAP_OF_NESTED_MAPS_BIN_NAME,
ctx=[
cdt_ctx.cdt_ctx_all_children(),
cdt_ctx.cdt_ctx_all_children()
],
expr=ResultRemove().compile(),
flags=aerospike.CDT_MODIFY_DEFAULT
)
]

with self.expected_context_for_pos_tests:
self.as_connection.operate(self.key, ops)

_, _, bins = self.as_connection.get(self.key)
assert bins[self.MAP_OF_NESTED_MAPS_BIN_NAME] == {
"Day1": {
},
"Day2": {
},
"Day3": {
}
}
Loading