here’s an example to demonstrate what i’m looking for
import aerospike
from aerospike_helpers import cdt_ctx, expressions
from aerospike_helpers.expressions.arithmetic import Add
from aerospike_helpers.operations import expression_operations
ac = aerospike.client({'hosts': [('localhost', 3000)]})
ac.connect()
key = ('test', 'test', 'test')
ac.remove(key)
ac.put(key, {'bin': {'vals': [], 'tokens': 1}})
def add_val_if_enough_tokens_and_empty(val):
ac.operate(
key,
[
expression_operations.expression_write(
'bin',
expressions.Cond(
expressions.GT(
expressions.MapGetByKey(
ctx=None,
return_type=aerospike.MAP_RETURN_VALUE,
value_type=expressions.resources.ResultType.INTEGER,
key='tokens',
bin=expressions.MapBin('bin')
),
0
),
expressions.Cond(
expressions.GT(
expressions.ListSize(
ctx=[cdt_ctx.cdt_ctx_map_key('vals')],
bin=expressions.MapBin('bin')
),
0
),
expressions.Unknown(),
expressions.ListAppend(
ctx=[cdt_ctx.cdt_ctx_map_key('vals')],
policy=None,
value=val,
bin=expressions.MapBin('bin')
)
),
expressions.Unknown()
).compile(),
# aerospike.EXP_WRITE_EVAL_NO_FAIL
# aerospike.EXP_WRITE_POLICY_NO_FAIL
)
]
)
for _ in range(2):
print(ac.get(key)[2])
try:
add_val_if_enough_tokens_and_empty('val')
except aerospike.exception.OpNotApplicable as e:
print(f'op failed because {e}')
with output
{'bin': {'vals': [], 'tokens': 1}}
{'bin': {'vals': ['val'], 'tokens': 1}}
op failed because (26, '127.0.0.1:3000 AEROSPIKE_ERR_OP_NOT_APPLICABLE', 'src/main/client/operate.c', 813, False)
so here, i want to only add a value to vals
if tokens
is greater than 0 and vals
is empty, which is achieved with this, but i cannot transactionally know which one of them was not satisfied on failure (unless of course i fetch the record, which isn’t transactional), so would like to have something like this
import aerospike
from aerospike_helpers import cdt_ctx, expressions
from aerospike_helpers.operations import expression_operations
ac = aerospike.client({'hosts': [('localhost', 3000)]})
ac.connect()
key = ('test', 'test', 'test')
ac.remove(key)
ac.put(key, {'bin': {'vals': [], 'tokens': 1}})
def add_val_if_enough_tokens_and_empty(val):
ac.operate(
key,
[
expression_operations.expression_write(
'bin',
expressions.Cond(
expressions.GT(
expressions.MapGetByKey(
ctx=None,
return_type=aerospike.MAP_RETURN_VALUE,
value_type=expressions.resources.ResultType.INTEGER,
key='tokens',
bin=expressions.MapBin('bin')
),
0
),
expressions.Cond(
expressions.GT(
expressions.ListSize(
ctx=[cdt_ctx.cdt_ctx_map_key('vals')],
bin=expressions.MapBin('bin')
),
0
),
expressions.Error("vals isn't empty"),
expressions.ListAppend(
ctx=[cdt_ctx.cdt_ctx_map_key('vals')],
policy=None,
value=val,
bin=expressions.MapBin('bin')
)
),
expressions.Error("no tokens")
).compile(),
# aerospike.EXP_WRITE_EVAL_NO_FAIL
# aerospike.EXP_WRITE_POLICY_NO_FAIL
)
]
)
for _ in range(2):
print(ac.get(key)[2])
try:
add_val_if_enough_tokens_and_empty('val')
except aerospike.exception.OpNotApplicable as e:
print(f'op failed because {e}')
with the expressions.Error
’s which would be parsable from the resulting aerospike.exception.OpNotApplicable
or some other error type specifically for this sorta thing
does this feature make sense?