Path expressions usage examples
For the complete documentation index see: llms.txt
All documentation pages available in markdown.
This page contains examples of using path expressions to select elements from a document modeled in Aerospike as nested map and list structures.
Each operation’s Example shows the code in nine tabs: Aerospike Expression Language (AEL) text on the Java SDK and Python SDK tabs, and the Exp builder on the other seven. See the AEL reference for AEL grammar.
The quickstart guide covers a common use case: filtering and selecting products and their in-stock variants. This section highlights additional capabilities supported by path expressions.
Accessing current element data from a map key, list index or element value with a LoopVar
When using a path expression to match Map or List elements, you can assign data from the matched element to loop variables.
Example: Select only products whose key starts with SKU 1000.
Record record = session.query(key) .bin("catalog") .selectFrom("$.catalog.inventory.*[?(@key =~ /1000.*/)].getTree()") .execute() .getFirstRecord();
System.out.println(record.getMap("catalog"));result = ( session.query(key) .bin("catalog") .select_from( "$.catalog.inventory.*[?(@key =~ /1000.*/)].getTree()" ) .first_or_raise())print(result.record.bins["catalog"])use aerospike::expressions::{exp_string_loop_var, regex_compare, LoopVarPart};use aerospike::operations::cdt_context::{ctx_all_children, ctx_all_children_with_filter};use aerospike::operations::path::{self, SelectFlag};use aerospike::{RegexFlag, WritePolicy};
let filter_on_key = regex_compare("1000.*".to_string(), RegexFlag::NONE as i64, exp_string_loop_var(LoopVarPart::MAP_KEY));
// Operationlet record = client.operate(&WritePolicy::default(), &key, &[ path::select_by_path("catalog", SelectFlag::MATCHING_TREE, [ ctx_all_children(), ctx_all_children_with_filter(filter_on_key), ]),]).await?;
println!("{}", record.bins["catalog"]);Exp filterOnKey = Exp.RegexCompare("1000.*", RegexFlag.NONE, Exp.StringLoopVar(LoopVarPart.MAP_KEY));
// OperationRecord record = client.Operate(null, key, CDTOperation.SelectByPath("catalog", SelectFlag.MATCHING_TREE, CTX.AllChildren(), CTX.AllChildrenWithFilter(filterOnKey) ));Console.WriteLine(System.Text.Json.JsonSerializer.Serialize((Dictionary<object, object>)record.GetMap("catalog"), new System.Text.Json.JsonSerializerOptions { WriteIndented = true }));// Requires: import as "github.com/aerospike/aerospike-client-go/v8"filterOnKey := as.ExpRegexCompare("1000.*", as.ExpRegexFlagNONE, as.ExpStringLoopVar(as.MAP_KEY))// Operationrecord, err := client.Operate(nil, key, as.SelectByPath("catalog", as.EXP_PATH_SELECT_MATCHING_TREE, as.CtxAllChildren(), as.CtxAllChildrenWithFilter(filterOnKey), ),)
fmt.Println(record.Bins["catalog"])const filterOnKey = exp.cmpRegex(Aerospike.regex.BASIC, "1000.*", exp.loopVarStr(exp.loopVarPart.KEY));
const ctx = new Context().addAllChildren().addAllChildrenWithFilter(filterOnKey)
const ops = [ op.selectByPath('catalog', exp.pathSelectFlags.MATCHING_TREE, ctx)]
const result = await client.operate(key, ops)
console.log(result.bins.catalog)// Filteras_exp_build(filter_on_key, as_exp_cmp_regex( 0, "1000.*", as_exp_loopvar_str(AS_EXP_LOOPVAR_KEY)));
// Operation
as_cdt_ctx ctx;as_cdt_ctx_inita(&ctx, 2);as_cdt_ctx_add_all_children(&ctx);as_cdt_ctx_add_all_children_with_filter(&ctx, filter_on_key);
as_operations ops;as_operations_inita(&ops, 1);as_operations_select_by_path(&err, &ops, "catalog", &ctx, AS_EXP_PATH_SELECT_MATCHING_TREE);
as_record* rec = NULL;aerospike_key_operate(&as, &err, NULL, &key, &ops, &rec);
// Print in JSON format
as_map* map = as_record_get_map(rec, "catalog");if (map) { char* map_str = as_val_tostring((as_val*)map); printf("%s\n", map_str); free(map_str);} else { printf("No data returned\n");}Exp filterOnKey = Exp.regexCompare("1000.*", RegexFlag.NONE, Exp.stringLoopVar(LoopVarPart.MAP_KEY));
// OperationRecord record = client.operate(null, key, CdtOperation.selectByPath("catalog", SelectFlags.MATCHING_TREE, CTX.allChildren(), CTX.allChildrenWithFilter(filterOnKey)));
System.out.println(record.getMap("catalog"));import aerospikefrom aerospike_helpers import expressions as exp, cdt_ctxfrom aerospike_helpers.operations import operations
filter_on_key = exp.CmpRegex(aerospike.REGEX_NONE, "1000.*", exp.LoopVarStr(aerospike.EXP_LOOPVAR_KEY)).compile()
ctx = [ cdt_ctx.cdt_ctx_all_children(), cdt_ctx.cdt_ctx_all_children_with_filter(filter_on_key)]# Operationops = [ operations.select_by_path( "catalog", ctx, aerospike.EXP_PATH_SELECT_MATCHING_TREE )](key, meta, bins) = client.operate(key, ops)import jsonprint(json.dumps(bins["catalog"], indent=4))Expected output:
{ "inventory": { "10000001": { ... }, "10000002": { ... } }}- ✅ Only products whose keys start with
1000are included.
Use alternative return modes with SelectFlags
Sometimes you don’t want the full tree, but just the map keys or values.
Example: Return only the SKUs of in-stock variants for featured products that follow a map/dictionary structure.
NO_FAIL is included here because variants is mixed in this dataset: some products store
variants as a map, while others store variants as a list. Since MAP_KEY only applies to
map-backed branches, NO_FAIL skips incompatible branches instead of failing the whole operation.
// Product-level filter: featured == trueExp filterOnFeatured = Exp.eq( MapExp.getByKey(MapReturnType.VALUE, Exp.Type.BOOL, Exp.val("featured"), Exp.mapLoopVar(LoopVarPart.VALUE)), Exp.val(true));
// Variant-level filter: quantity > 0Exp filterOnVariantInventory = Exp.gt( MapExp.getByKey(MapReturnType.VALUE, Exp.Type.INT, Exp.val("quantity"), Exp.mapLoopVar(LoopVarPart.VALUE)), Exp.val(0));
// AEL takes no NO_FAIL on a read, so this path uses the builder.Record readResult = session.query(key) .bin("catalog") .onEachChild() .onEachChild(filterOnFeatured) .onMapKey("variants") .onEachChild(filterOnVariantInventory) .collectKeys(opts -> opts.noFail(true)) .execute() .getFirstRecord();
System.out.println(readResult.getList("catalog"));from aerospike_sdk import Exp, ExpType, LoopVarPart, MapReturnType
# Product-level filter: featured == truefilter_on_featured = Exp.eq( Exp.map_get_by_key(MapReturnType.VALUE, ExpType.BOOL, Exp.val("featured"), Exp.map_loop_var(LoopVarPart.VALUE), []), Exp.val(True))
# Variant-level filter: quantity > 0filter_on_variant_inventory = Exp.gt( Exp.map_get_by_key(MapReturnType.VALUE, ExpType.INT, Exp.val("quantity"), Exp.map_loop_var(LoopVarPart.VALUE), []), Exp.val(0))
# AEL takes no NO_FAIL on a read, so this path uses the builder.result = ( session.query(key) .bin("catalog") .on_each_child() .on_each_child_where(filter_on_featured) .on_map_key("variants") .on_each_child_where(filter_on_variant_inventory) .collect_map_keys(no_fail=True) .first_or_raise())print(result.record.bins["catalog"])use aerospike::operations::cdt_context::{ctx_all_children, ctx_all_children_with_filter, ctx_map_key};use aerospike::operations::path::{self, SelectFlag};use aerospike::{as_val, WritePolicy};
// Operationlet read_result = client.operate(&WritePolicy::default(), &key, &[ path::select_by_path("catalog", SelectFlag(SelectFlag::MAP_KEY.0 | SelectFlag::NO_FAIL.0), [ ctx_all_children(), ctx_all_children_with_filter(filter_on_featured), ctx_map_key(as_val!("variants")), ctx_all_children_with_filter(filter_on_variant_inventory), ]),]).await?;
println!("{}", read_result.bins["catalog"]);// OperationRecord record = client.Operate(null, key, CDTOperation.SelectByPath("catalog", SelectFlag.MAP_KEY | SelectFlag.NO_FAIL, CTX.AllChildren(), // dive into all products CTX.AllChildrenWithFilter(filterOnFeatured), // only featured products CTX.MapKey(Value.Get("variants")), // dive into variants CTX.AllChildrenWithFilter(filterOnVariantInventory) // only in-stock variants ));Console.WriteLine(System.Text.Json.JsonSerializer.Serialize((List<object>)record.GetList("catalog"), new System.Text.Json.JsonSerializerOptions { WriteIndented = true }));// Requires: import as "github.com/aerospike/aerospike-client-go/v8"// OperationreadResult, err := client.Operate(nil, key, as.SelectByPath("catalog", as.EXP_PATH_SELECT_MAP_KEY|as.EXP_PATH_SELECT_NO_FAIL, as.CtxAllChildren(), as.CtxAllChildrenWithFilter(filterOnFeatured), as.CtxMapKey(as.NewValue("variants")), as.CtxAllChildrenWithFilter(filterOnVariantInventory), ),)fmt.Println(readResult.Bins["catalog"])const context = new Context()
context.addAllChildren()context.addAllChildrenWithFilter(filterOnFeatured)context.addMapKey('variants')context.addAllChildrenWithFilter(filterOnVariantInventory)
// Operationconst ops = [ op.selectByPath("catalog", exp.pathSelectFlags.MAP_KEY | exp.pathSelectFlags.NO_FAIL, context)]
const result = await client.operate(key, ops)
console.log(result.bins.catalog)// filter_on_featured and filter_on_variant_inventory as in the quickstart
// Operationas_cdt_ctx ctx;as_cdt_ctx_inita(&ctx, 4);as_cdt_ctx_add_all_children(&ctx);as_cdt_ctx_add_all_children_with_filter(&ctx, filter_on_featured);as_cdt_ctx_add_map_key(&ctx, (as_val*)as_string_new("variants", false));as_cdt_ctx_add_all_children_with_filter(&ctx, filter_on_variant_inventory);
as_operations ops;as_operations_inita(&ops, 1);as_operations_select_by_path(&err, &ops, "catalog", &ctx, AS_EXP_PATH_SELECT_MAP_KEY | AS_EXP_PATH_SELECT_NO_FAIL);
as_record* rec = NULL;aerospike_key_operate(&as, &err, NULL, &key, &ops, &rec);
as_list* list = as_record_get_list(rec, "catalog");if (list) { char* list_str = as_val_tostring((as_val*)list); printf("%s\n", list_str); free(list_str);} else { printf("No data returned\n");}// OperationRecord readResult = client.operate(null, key, CdtOperation.selectByPath("catalog", SelectFlags.MAP_KEY | SelectFlags.NO_FAIL, CTX.allChildren(), CTX.allChildrenWithFilter(filterOnFeatured), CTX.mapKey(Value.get("variants")), CTX.allChildrenWithFilter(filterOnVariantInventory) ));
System.out.println(readResult.getList("catalog"));# Operationops = [ operations.select_by_path( "catalog", [ cdt_ctx.cdt_ctx_all_children(), cdt_ctx.cdt_ctx_all_children_with_filter(filter_on_featured), cdt_ctx.cdt_ctx_map_key('variants'), cdt_ctx.cdt_ctx_all_children_with_filter(filter_on_variant_inventory) ], aerospike.EXP_PATH_SELECT_MAP_KEY | aerospike.EXP_PATH_SELECT_NO_FAIL)]
(key, meta, bins) = client.operate(key, ops)print(bins["catalog"])Expected output:
["2001","2003"]- ✅ Only the keys from SKU
10000001,Classic T-Shirtare returned. - ⚠️ Item
50000009,Smart TV, has list-backed variants, so there are no map keys to return.
Modify nested elements with modifyByPath
You can update selected values in place.
Example: Increase quantity by 10 for all in-stock variants of featured products.
// Increment quantity by 10session.update(key) .bin("catalog") .upsertFrom("$.catalog.inventory.*[?(@.featured == true)]" + ".variants.*[?(@.quantity > 0)].quantity.modify(@ + 10)") .execute();
Record readResult = session.query(key).execute().getFirstRecord();
System.out.println(readResult.getMap("catalog"));# Increment quantity by 10session.update(key).bin("catalog").upsert_from( "$.catalog.inventory.*[?(@.featured == true)]" ".variants.*[?(@.quantity > 0)].quantity.modify(@ + 10)").execute()
record = session.query(key).first_or_raise().recordprint(record.bins["catalog"])use aerospike::expressions::{exp_map_loop_var, int_val, maps as map_exp, num_add, string_val, ExpType, LoopVarPart};use aerospike::operations::cdt_context::{ctx_all_children, ctx_all_children_with_filter, ctx_map_key};use aerospike::operations::maps::MapPolicy;use aerospike::operations::path::{self, ModifyFlag};use aerospike::{as_val, Bins, MapReturnType, ReadPolicy, WritePolicy};
// Increment quantity by 10 and return the modified maplet increment_quantity = map_exp::put(&MapPolicy::default(), string_val("quantity".into()), num_add(vec![ map_exp::get_by_key(MapReturnType::Value, ExpType::INT, string_val("quantity".into()), exp_map_loop_var(LoopVarPart::VALUE), &[]), int_val(10), ]), exp_map_loop_var(LoopVarPart::VALUE), &[]);
client.operate(&WritePolicy::default(), &key, &[ path::modify_by_path("catalog", ModifyFlag::DEFAULT, increment_quantity, [ ctx_all_children(), ctx_all_children_with_filter(filter_on_featured), ctx_map_key(as_val!("variants")), ctx_all_children_with_filter(filter_on_variant_inventory), ]),]).await?;
let read_result = client.get(&ReadPolicy::default(), &key, Bins::All).await?;
println!("{}", read_result.bins["catalog"]);// Increment quantity by 10 and return the modified mapExp incrementExp = MapExp.Put( MapPolicy.Default, Exp.Val("quantity"), Exp.Add( MapExp.GetByKey(MapReturnType.VALUE, Exp.Type.INT, Exp.Val("quantity"), Exp.MapLoopVar(LoopVarPart.VALUE)), Exp.Val(10) ), Exp.MapLoopVar(LoopVarPart.VALUE));
Expression modifyExpression = Exp.Build(incrementExp);
// Write the modified map to a new binRecord record = client.Operate(null, key, CDTOperation.ModifyByPath("catalog", ModifyFlag.DEFAULT, modifyExpression, CTX.AllChildren(), // dive into all products CTX.AllChildrenWithFilter(filterOnFeatured), // only featured products CTX.MapKey(Value.Get("variants")), // dive into variants CTX.AllChildrenWithFilter(filterOnVariantInventory) // only in-stock variants ));
// Read back the updated recordRecord updatedRecord = client.Get(null, key);Console.WriteLine(System.Text.Json.JsonSerializer.Serialize((Dictionary<object, object>)updatedRecord.GetMap("catalog"), new System.Text.Json.JsonSerializerOptions { WriteIndented = true }));// Requires: import as "github.com/aerospike/aerospike-client-go/v8"// Increment quantity by 10 and return the modified mapincrementQuantity := as.ExpMapPut( as.DefaultMapPolicy(), as.ExpStringVal("quantity"), as.ExpNumAdd( as.ExpMapGetByKey( as.MapReturnType.VALUE, as.ExpTypeINT, as.ExpStringVal("quantity"), as.ExpMapLoopVar(as.VALUE), ), as.ExpIntVal(10), ), as.ExpMapLoopVar(as.VALUE),)
_, err = client.Operate(nil, key, as.ModifyByPath("catalog", as.EXP_PATH_MODIFY_DEFAULT, incrementQuantity, as.CtxAllChildren(), as.CtxAllChildrenWithFilter(filterOnFeatured), as.CtxMapKey(as.NewValue("variants")), as.CtxAllChildrenWithFilter(filterOnVariantInventory), ),)
record, _ := client.Get(nil, key)fmt.Println(record.Bins["catalog"])// Increment quantity by 10 and return the modified mapconst incrementQuantity = exp.maps.put( exp.loopVarMap(exp.loopVarPart.VALUE), exp.add( exp.maps.getByKey( exp.loopVarMap(exp.loopVarPart.VALUE), exp.str('quantity'), exp.type.INT, maps.returnType.VALUE ), exp.int(10) ), exp.str('quantity'))
const context = new Context()
context.addAllChildren()context.addAllChildrenWithFilter(filterOnFeatured)context.addMapKey('variants')context.addAllChildrenWithFilter(filterOnVariantInventory)
// Operationconst ops = [ op.modifyByPath("catalog", incrementQuantity, exp.pathModifyFlags.DEFAULT, context)]
await client.operate(key, ops)
const result = await client.get(key)
console.log(result.bins.catalog)// Configure our increment operation// Increment quantity by 10 and return the modified mapas_exp_build(increment_quantity, as_exp_map_put( NULL, NULL, as_exp_str("quantity"), as_exp_add( as_exp_map_get_by_key( NULL, AS_MAP_RETURN_VALUE, AS_EXP_TYPE_INT, as_exp_str("quantity"), as_exp_loopvar_map(AS_EXP_LOOPVAR_VALUE) ), as_exp_int(10) ), as_exp_loopvar_map(AS_EXP_LOOPVAR_VALUE)));
// filter_on_featured and filter_on_variant_inventory as in the quickstart
// Operation
as_cdt_ctx ctx;as_cdt_ctx_inita(&ctx, 4);as_cdt_ctx_add_all_children(&ctx);as_cdt_ctx_add_all_children_with_filter(&ctx, filter_on_featured);as_cdt_ctx_add_map_key(&ctx, (as_val*)as_string_new("variants", false));as_cdt_ctx_add_all_children_with_filter(&ctx, filter_on_variant_inventory);
as_operations ops;as_operations_inita(&ops, 1);as_operations_modify_by_path(&err, &ops, "catalog", &ctx, increment_quantity, AS_EXP_PATH_MODIFY_DEFAULT);
as_record* rec = NULL;aerospike_key_operate(&as, &err, NULL, &key, &ops, &rec);
aerospike_key_get(&as, &err, NULL, &key, &rec);
as_map* map = as_record_get_map(rec, "catalog");if (map) { char* map_str = as_val_tostring((as_val*)map); printf("%s\n", map_str); free(map_str);} else { printf("No data returned\n");}// Increment quantity by 10 and return the modified mapExpression incrementQuantity = Exp.build( MapExp.put( MapPolicy.Default, Exp.val("quantity"), // key to update Exp.add( // new value: current quantity + 10 MapExp.getByKey(MapReturnType.VALUE, Exp.Type.INT, Exp.val("quantity"), Exp.mapLoopVar(LoopVarPart.VALUE)), Exp.val(10) ), Exp.mapLoopVar(LoopVarPart.VALUE) // map to update ));
client.operate(null, key, CdtOperation.modifyByPath("catalog", ModifyFlags.DEFAULT, incrementQuantity, CTX.allChildren(), CTX.allChildrenWithFilter(filterOnFeatured), CTX.mapKey(Value.get("variants")), CTX.allChildrenWithFilter(filterOnVariantInventory) ));
Record readResult = client.get(null, key);
System.out.println(readResult.getMap("catalog"));add_exp = exp.Add( exp.MapGetByKey( ctx=None, return_type=MAP_RETURN_VALUE, value_type=exp.ResultType.INTEGER, key=exp.Val("quantity"), bin=exp.LoopVarMap(aerospike.EXP_LOOPVAR_VALUE) ), exp.Val(10))
# Increment quantity by 10 and return the modified mapincrement_quantity = exp.MapPut( ctx=None, policy=None, key="quantity", value=add_exp, bin=exp.LoopVarMap(aerospike.EXP_LOOPVAR_VALUE)).compile()
ops = [ operations.modify_by_path( "catalog", [ cdt_ctx.cdt_ctx_all_children(), cdt_ctx.cdt_ctx_all_children_with_filter(filter_on_featured), cdt_ctx.cdt_ctx_map_key('variants'), cdt_ctx.cdt_ctx_all_children_with_filter(filter_on_variant_inventory) ], increment_quantity, aerospike.EXP_PATH_MODIFY_DEFAULT)]
client.operate(key, ops)
(key, meta, bins) = client.get(key)print(json.dumps(bins["catalog"], indent=4))Expected Record Modification (diff excerpt):
{ "inventory": { "10000001": { "category": "clothing", "featured": true, "name": "Classic T-Shirt", "description": "A lightweight cotton T-shirt perfect for everyday wear.", "variants": { "2001": { "size": "S", "price": 25, "quantity": 110 }, // 100 -> 110 "2003": { "size": "L", "price": 27, "quantity": 60 } // 50 -> 60 } }, "50000009": { "category": "electronics", "featured": true, "name": "Smart TV", "description": "Ultra HD smart television with built-in streaming apps.", "variants": [ { "sku": 3007, "spec": "1080p", "price": 199, "quantity": 70 }, // 60 -> 70 { "sku": 3008, "spec": "4K", "price": 399, "quantity": 40 } // 30 -> 40 ] }, }}- ✅ Inventories for in-stock variants are incremented directly on the server.
Remove elements with removeResult
Use removeResult with modifyByPath to delete elements selected by the path context.
- If the path/filter matches one element, one element is removed.
- If the path/filter matches multiple elements, all matches are removed.
Example: Remove all out-of-stock variants (quantity == 0) from featured products.
// Remove each variant where quantity == 0.session.update(key) .bin("catalog") .upsertFrom("$.catalog.inventory.*[?(@.featured == true)]" + ".variants.*[?(@.quantity == 0)].remove():NO_FAIL") .execute();# Remove each variant where quantity == 0.session.update(key).bin("catalog").upsert_from( "$.catalog.inventory.*[?(@.featured == true)]" ".variants.*[?(@.quantity == 0)].remove():NO_FAIL").execute()use aerospike::expressions::{eq, exp_map_loop_var, exp_remove_result, int_val, maps as map_exp, string_val, ExpType, LoopVarPart};use aerospike::operations::cdt_context::{ctx_all_children, ctx_all_children_with_filter, ctx_map_key};use aerospike::operations::path::{self, ModifyFlag};use aerospike::{as_val, MapReturnType, WritePolicy};
// Match variants where quantity == 0.let out_of_stock_filter = eq( map_exp::get_by_key(MapReturnType::Value, ExpType::INT, string_val("quantity".into()), exp_map_loop_var(LoopVarPart::VALUE), &[]), int_val(0));
// Remove each matched element.let remove_matched = exp_remove_result();
client.operate(&WritePolicy::default(), &key, &[ path::modify_by_path("catalog", ModifyFlag::NO_FAIL, remove_matched, [ ctx_all_children(), ctx_all_children_with_filter(filter_on_featured), ctx_map_key(as_val!("variants")), ctx_all_children_with_filter(out_of_stock_filter), ]),]).await?;// Match variants where quantity == 0.Exp outOfStockFilter = Exp.EQ( MapExp.GetByKey(MapReturnType.VALUE, Exp.Type.INT, Exp.Val("quantity"), Exp.MapLoopVar(LoopVarPart.VALUE)), Exp.Val(0));
// Remove each matched element.Expression removeMatched = Exp.Build(Exp.RemoveResult());
client.Operate(null, key, CDTOperation.ModifyByPath("catalog", ModifyFlag.NO_FAIL, removeMatched, CTX.AllChildren(), CTX.AllChildrenWithFilter(filterOnFeatured), CTX.MapKey(Value.Get("variants")), CTX.AllChildrenWithFilter(outOfStockFilter) ));// Requires: import as "github.com/aerospike/aerospike-client-go/v8"// Match variants where quantity == 0.outOfStockFilter := as.ExpEq( as.ExpMapGetByKey( as.MapReturnType.VALUE, as.ExpTypeINT, as.ExpStringVal("quantity"), as.ExpMapLoopVar(as.VALUE), ), as.ExpIntVal(0),)
// Remove each matched element.removeMatched := as.ExpRemoveResult()
_, err = client.Operate(nil, key, as.ModifyByPath("catalog", as.EXP_PATH_MODIFY_NO_FAIL, removeMatched, as.CtxAllChildren(), as.CtxAllChildrenWithFilter(filterOnFeatured), as.CtxMapKey(as.NewValue("variants")), as.CtxAllChildrenWithFilter(outOfStockFilter), ),)// Match variants where quantity == 0.const outOfStockFilter = exp.eq( exp.maps.getByKey( exp.loopVarMap(exp.loopVarPart.VALUE), exp.str('quantity'), exp.type.INT, maps.returnType.VALUE ), exp.int(0))
const context = new Context()context.addAllChildren()context.addAllChildrenWithFilter(filterOnFeatured)context.addMapKey('variants')context.addAllChildrenWithFilter(outOfStockFilter)
// resultRemove marks each matched element for deletion.const ops = [ op.modifyByPath( 'catalog', exp.resultRemove(), exp.pathModifyFlags.NO_FAIL, context )]
await client.operate(key, ops)// Match variants where quantity == 0.as_exp_build(out_of_stock_filter, as_exp_cmp_eq( as_exp_map_get_by_key( NULL, AS_MAP_RETURN_VALUE, AS_EXP_TYPE_INT, as_exp_str("quantity"), as_exp_loopvar_map(AS_EXP_LOOPVAR_VALUE) ), as_exp_int(0) ));
// Remove each matched element.as_exp_build(remove_matched, as_exp_result_remove());
as_cdt_ctx ctx;as_cdt_ctx_inita(&ctx, 4);as_cdt_ctx_add_all_children(&ctx);as_cdt_ctx_add_all_children_with_filter(&ctx, filter_on_featured);as_cdt_ctx_add_map_key(&ctx, (as_val*)as_string_new("variants", false));as_cdt_ctx_add_all_children_with_filter(&ctx, out_of_stock_filter);
as_operations ops;as_operations_inita(&ops, 1);as_operations_modify_by_path( &err, &ops, "catalog", &ctx, remove_matched, AS_EXP_PATH_MODIFY_NO_FAIL);
as_record* rec = NULL;aerospike_key_operate(&as, &err, NULL, &key, &ops, &rec);import com.aerospike.client.exp.Expression;
// Match variants where quantity == 0.Expression outOfStockFilter = Exp.build( Exp.eq( MapExp.getByKey( MapReturnType.VALUE, Exp.Type.INT, Exp.val("quantity"), Exp.mapLoopVar(LoopVarPart.VALUE) ), Exp.val(0) ));
// removeResult marks each matched element for deletion.Expression removeMatched = Exp.build(Exp.removeResult());
client.operate(null, key, CdtOperation.modifyByPath("catalog", ModifyFlags.NO_FAIL, removeMatched, CTX.allChildren(), CTX.allChildrenWithFilter(filterOnFeatured), CTX.mapKey(Value.get("variants")), CTX.allChildrenWithFilter(outOfStockFilter) ));# Match variants where quantity == 0.out_of_stock_filter = exp.Eq( exp.MapGetByKey( ctx=None, return_type=MAP_RETURN_VALUE, value_type=exp.ResultType.INTEGER, key=exp.Val("quantity"), bin=exp.LoopVarMap(aerospike.EXP_LOOPVAR_VALUE) ), exp.Val(0)).compile()
# Remove each matched element.remove_result = exp.RemoveResult().compile()
ops = [ operations.modify_by_path( "catalog", [ cdt_ctx.cdt_ctx_all_children(), cdt_ctx.cdt_ctx_all_children_with_filter(filter_on_featured), cdt_ctx.cdt_ctx_map_key("variants"), cdt_ctx.cdt_ctx_all_children_with_filter(out_of_stock_filter) ], remove_result, aerospike.EXP_PATH_MODIFY_NO_FAIL )]client.operate(key, ops)Expected record modification (excerpt):
{ "inventory": { "10000001": { "variants": { "2001": { "size": "S", "price": 25, "quantity": 100 }, "2003": { "size": "L", "price": 27, "quantity": 50 } } }, "50000009": { "variants": [ { "sku": 3007, "spec": "1080p", "price": 199, "quantity": 60 }, { "sku": 3008, "spec": "4K", "price": 399, "quantity": 30 } ] }, "50000006": { "variants": {} } }}✅ Out-of-stock entries are removed from matching map/list branches.
Combine multiple filters
Filters can be chained with AND / OR with each expression pulling data from the current matched element into a different loop variable.
Example: Select variants that are in stock and have price < 50.
Record record = session.query(key) .bin("catalog") .selectFrom("$.catalog.inventory.*[?(@.featured == true)]" + ".variants.*[?(@.quantity > 0 and @.price < 50)].getTree()") .execute() .getFirstRecord();
System.out.println(record.getMap("catalog"));result = ( session.query(key) .bin("catalog") .select_from( "$.catalog.inventory.*[?(@.featured == true)]" ".variants.*[?(@.quantity > 0 and @.price < 50)].getTree()" ) .first_or_raise())print(result.record.bins["catalog"])use aerospike::expressions::{and, exp_map_loop_var, gt, int_val, lt, maps as map_exp, string_val, ExpType, LoopVarPart};use aerospike::operations::cdt_context::{ctx_all_children, ctx_all_children_with_filter, ctx_map_key};use aerospike::operations::path::{self, SelectFlag};use aerospike::{as_val, MapReturnType, WritePolicy};
let filter_on_cheap_in_stock = and(vec![ gt( map_exp::get_by_key(MapReturnType::Value, ExpType::INT, string_val("quantity".into()), exp_map_loop_var(LoopVarPart::VALUE), &[]), int_val(0)), lt( map_exp::get_by_key(MapReturnType::Value, ExpType::INT, string_val("price".into()), exp_map_loop_var(LoopVarPart::VALUE), &[]), int_val(50)),]);
// Operationlet record = client.operate(&WritePolicy::default(), &key, &[ path::select_by_path("catalog", SelectFlag::MATCHING_TREE, [ ctx_all_children(), ctx_all_children_with_filter(filter_on_featured), ctx_map_key(as_val!("variants")), ctx_all_children_with_filter(filter_on_cheap_in_stock), ]),]).await?;
println!("{}", record.bins["catalog"]);Exp filterOnCheapInStock = Exp.And( Exp.GT( MapExp.GetByKey(MapReturnType.VALUE, Exp.Type.INT, Exp.Val("quantity"), Exp.MapLoopVar(LoopVarPart.VALUE) ), Exp.Val(0) ), Exp.LT( MapExp.GetByKey(MapReturnType.VALUE, Exp.Type.INT, Exp.Val("price"), Exp.MapLoopVar(LoopVarPart.VALUE) ), Exp.Val(50) ));
// OperationRecord record = client.Operate(null, key, CDTOperation.SelectByPath("catalog", SelectFlag.MATCHING_TREE, CTX.AllChildren(), CTX.AllChildrenWithFilter(filterOnFeatured), CTX.MapKey(Value.Get("variants")), CTX.AllChildrenWithFilter(filterOnCheapInStock) ));
Console.WriteLine(System.Text.Json.JsonSerializer.Serialize((Dictionary<object, object>)record.GetMap("catalog"), new System.Text.Json.JsonSerializerOptions { WriteIndented = true }));// Requires: import as "github.com/aerospike/aerospike-client-go/v8"filterOnCheapInStock := as.ExpAnd( as.ExpGreater( as.ExpMapGetByKey( as.MapReturnType.VALUE, as.ExpTypeINT, as.ExpStringVal("quantity"), as.ExpMapLoopVar(as.VALUE), ), as.ExpIntVal(0), ), as.ExpLess( as.ExpMapGetByKey( as.MapReturnType.VALUE, as.ExpTypeINT, as.ExpStringVal("price"), as.ExpMapLoopVar(as.VALUE), ), as.ExpIntVal(50), ),)// OperationcheapInStock, err := client.Operate(nil, key, as.SelectByPath("catalog", as.EXP_PATH_SELECT_MATCHING_TREE, as.CtxAllChildren(), as.CtxAllChildrenWithFilter(filterOnFeatured), as.CtxMapKey(as.NewValue("variants")), as.CtxAllChildrenWithFilter(filterOnCheapInStock), ),)fmt.Println(cheapInStock.Bins["catalog"])const filterOnCheapInStock = exp.and( exp.gt( exp.maps.getByKey( exp.loopVarMap(exp.loopVarPart.VALUE), // the value of the current element is assigned to a loop variable whose data type is a map exp.str('quantity'), exp.type.INT, maps.returnType.VALUE ), exp.int(0) ), exp.lt( exp.maps.getByKey( exp.loopVarMap(exp.loopVarPart.VALUE), // the value of the current element is assigned to a loop variable whose data type is a map exp.str('price'), exp.type.INT, maps.returnType.VALUE ), exp.int(50) ))
const context = new Context()
context.addAllChildren()context.addAllChildrenWithFilter(filterOnFeatured)
context.addMapKey('variants')context.addAllChildrenWithFilter(filterOnCheapInStock)
// Operationconst ops = [ op.selectByPath("catalog", exp.pathSelectFlags.MATCHING_TREE, context)]
const result = await client.operate(key, ops)
console.log(result.bins.catalog)as_exp_build(filter_on_cheap_in_stock, as_exp_and( as_exp_cmp_gt( as_exp_map_get_by_key( NULL, AS_MAP_RETURN_VALUE, AS_EXP_TYPE_INT, as_exp_str("quantity"), as_exp_loopvar_map(AS_EXP_LOOPVAR_VALUE) ), as_exp_int(0) ), as_exp_cmp_lt( as_exp_map_get_by_key( NULL, AS_MAP_RETURN_VALUE, AS_EXP_TYPE_INT, as_exp_str("price"), as_exp_loopvar_map(AS_EXP_LOOPVAR_VALUE) ), as_exp_int(50) )));
// Operation
as_cdt_ctx ctx;as_cdt_ctx_inita(&ctx, 4);as_cdt_ctx_add_all_children(&ctx);as_cdt_ctx_add_all_children_with_filter(&ctx, filter_on_featured);as_cdt_ctx_add_map_key(&ctx, (as_val*)as_string_new("variants", false));as_cdt_ctx_add_all_children_with_filter(&ctx, filter_on_cheap_in_stock);
as_operations ops;as_operations_inita(&ops, 1);as_operations_select_by_path(&err, &ops, "catalog", &ctx, AS_EXP_PATH_SELECT_MATCHING_TREE);
as_record* rec = NULL;aerospike_key_operate(&as, &err, NULL, &key, &ops, &rec);
// Print in JSON format
as_map* map = as_record_get_map(rec, "catalog");if (map) { char* map_str = as_val_tostring((as_val*)map); printf("%s\n", map_str); free(map_str);} else { printf("No data returned\n");}Exp filterOnCheapInStock = Exp.and( Exp.gt( MapExp.getByKey(MapReturnType.VALUE, Exp.Type.INT, Exp.val("quantity"), Exp.mapLoopVar(LoopVarPart.VALUE)), Exp.val(0)), Exp.lt( MapExp.getByKey(MapReturnType.VALUE, Exp.Type.INT, Exp.val("price"), Exp.mapLoopVar(LoopVarPart.VALUE)), Exp.val(50)));// OperationRecord record = client.operate(null, key, CdtOperation.selectByPath("catalog", SelectFlags.MATCHING_TREE, CTX.allChildren(), CTX.allChildrenWithFilter(filterOnFeatured), CTX.mapKey(Value.get("variants")), CTX.allChildrenWithFilter(filterOnCheapInStock)));
System.out.println(record.getMap("catalog"));filter_on_cheap_in_stock = exp.And( exp.GT( exp.MapGetByKey( ctx=None, return_type=MAP_RETURN_VALUE, value_type=exp.ResultType.INTEGER, key=exp.Val("quantity"), bin=exp.LoopVarMap(aerospike.EXP_LOOPVAR_VALUE) ), exp.Val(0) ), exp.LT( exp.MapGetByKey( ctx=None, return_type=MAP_RETURN_VALUE, value_type=exp.ResultType.INTEGER, key=exp.Val("price"), bin=exp.LoopVarMap(aerospike.EXP_LOOPVAR_VALUE) ), exp.Val(50) )).compile()
# Operationops = [ operations.select_by_path( "catalog", [ cdt_ctx.cdt_ctx_all_children(), cdt_ctx.cdt_ctx_all_children_with_filter(filter_on_featured), cdt_ctx.cdt_ctx_map_key("variants"), cdt_ctx.cdt_ctx_all_children_with_filter(filter_on_cheap_in_stock) ], aerospike.EXP_PATH_SELECT_MATCHING_TREE)](key, meta, bins) = client.operate(key, ops)print(bins["catalog"])Expected output:
{ "inventory": { "10000001": { "variants": { "2001": { "price": 25, "quantity": 100, "size": "S" }, "2003": { "price": 27, "quantity": 50, "size": "L" } } }, "50000006": { "variants": {} }, "50000009": { "variants": [] } }}- ✅ Only variants with price < 50 and quantity > 0 are returned.
- ❌ Item
10000002, Casual Polo Shirt, excluded (featured=false). - ❌ Variant
3001excluded from item50000006, Laptop Pro 14 (quantity=0). The item is featured, so it stays, with an emptyvariantsmap. - ❌ Item
50000009, Smart TV, both variants excluded (price > 50).
NO_FAIL: tolerate malformed product
If we add this item to the inventory bin:
"10000003": { "category": "clothing", "featured": true, "name": "Hooded Sweatshirt", "description": "Warm fleece hoodie with front pocket and adjustable hood.", "variants": "no variant"}The dataset now includes item 10000003 with variants: “no variant” (a string).
Any traversal that reaches variants and then tries to treat it as a
Map/List will hit a type mismatch and throw an error unless SelectFlags.NO_FAIL is set.
// Product-level filter: featured == trueExp filterOnFeatured = Exp.eq( MapExp.getByKey(MapReturnType.VALUE, Exp.Type.BOOL, Exp.val("featured"), Exp.mapLoopVar(LoopVarPart.VALUE)), Exp.val(true));
// Variant-level filter: quantity > 0Exp filterOnVariantInventory = Exp.gt( MapExp.getByKey(MapReturnType.VALUE, Exp.Type.INT, Exp.val("quantity"), Exp.mapLoopVar(LoopVarPart.VALUE)), Exp.val(0));
// AEL takes no NO_FAIL on a read, so this path uses the builder.Record noFailResponse = session.query(key) .bin("catalog") .onEachChild() .onEachChild(filterOnFeatured) .onMapKey("variants") .onEachChild(filterOnVariantInventory) .collectKeys(opts -> opts.noFail(true)) .execute() .getFirstRecord();
System.out.println(noFailResponse.getList("catalog"));from aerospike_sdk import Exp, ExpType, LoopVarPart, MapReturnType
# Product-level filter: featured == truefilter_on_featured = Exp.eq( Exp.map_get_by_key(MapReturnType.VALUE, ExpType.BOOL, Exp.val("featured"), Exp.map_loop_var(LoopVarPart.VALUE), []), Exp.val(True))
# Variant-level filter: quantity > 0filter_on_variant_inventory = Exp.gt( Exp.map_get_by_key(MapReturnType.VALUE, ExpType.INT, Exp.val("quantity"), Exp.map_loop_var(LoopVarPart.VALUE), []), Exp.val(0))
# AEL takes no NO_FAIL on a read, so this path uses the builder.result = ( session.query(key) .bin("catalog") .on_each_child() .on_each_child_where(filter_on_featured) .on_map_key("variants") .on_each_child_where(filter_on_variant_inventory) .collect_map_keys(no_fail=True) .first_or_raise())print(result.record.bins["catalog"])use aerospike::operations::cdt_context::{ctx_all_children, ctx_all_children_with_filter, ctx_map_key};use aerospike::operations::path::{self, SelectFlag};use aerospike::{as_val, WritePolicy};
// Operationlet no_fail_response = client.operate(&WritePolicy::default(), &key, &[ path::select_by_path("catalog", SelectFlag(SelectFlag::MAP_KEY.0 | SelectFlag::NO_FAIL.0), [ ctx_all_children(), ctx_all_children_with_filter(filter_on_featured), ctx_map_key(as_val!("variants")), ctx_all_children_with_filter(filter_on_variant_inventory), ]),]).await?;
println!("{}", no_fail_response.bins["catalog"]);// OperationRecord noFailResponse = client.Operate(null, key, CDTOperation.SelectByPath("catalog", SelectFlag.MAP_KEY | SelectFlag.NO_FAIL, CTX.AllChildren(), CTX.AllChildrenWithFilter(filterOnFeatured), CTX.MapKey(Value.Get("variants")), CTX.AllChildrenWithFilter(filterOnVariantInventory) ));Console.WriteLine(System.Text.Json.JsonSerializer.Serialize((List<object>)noFailResponse.GetList("catalog"), new System.Text.Json.JsonSerializerOptions { WriteIndented = true }));// Requires: import as "github.com/aerospike/aerospike-client-go/v8"// Operationrecord, _ := client.Operate(nil, key, as.SelectByPath("catalog", as.EXP_PATH_SELECT_MAP_KEY|as.EXP_PATH_SELECT_NO_FAIL, as.CtxAllChildren(), as.CtxAllChildrenWithFilter(filterOnFeatured), as.CtxMapKey(as.NewValue("variants")), as.CtxAllChildrenWithFilter(filterOnVariantInventory), ),)fmt.Println(record.Bins["catalog"])const context = new Context()
context.addAllChildren()context.addAllChildrenWithFilter(filterOnFeatured)context.addMapKey('variants')context.addAllChildrenWithFilter(filterOnVariantInventory)
// Operationconst ops = [ op.selectByPath("catalog", exp.pathSelectFlags.MAP_KEY | exp.pathSelectFlags.NO_FAIL, context)]
const result = await client.operate(key, ops)
console.log(result.bins.catalog)// filter_on_featured and filter_on_variant_inventory as in the quickstart
// Operationas_cdt_ctx ctx;as_cdt_ctx_inita(&ctx, 4);as_cdt_ctx_add_all_children(&ctx);as_cdt_ctx_add_all_children_with_filter(&ctx, filter_on_featured);as_cdt_ctx_add_map_key(&ctx, (as_val*)as_string_new("variants", false));as_cdt_ctx_add_all_children_with_filter(&ctx, filter_on_variant_inventory);
as_operations ops;as_operations_inita(&ops, 1);as_operations_select_by_path(&err, &ops, "catalog", &ctx, AS_EXP_PATH_SELECT_MAP_KEY | AS_EXP_PATH_SELECT_NO_FAIL);
as_record* rec = NULL;aerospike_key_operate(&as, &err, NULL, &key, &ops, &rec);
as_list* list = as_record_get_list(rec, "catalog");if (list) { char* list_str = as_val_tostring((as_val*)list); printf("%s\n", list_str); free(list_str);} else { printf("No data returned\n");}// OperationRecord noFailResponse = client.operate(null, key, CdtOperation.selectByPath("catalog", SelectFlags.MAP_KEY | SelectFlags.NO_FAIL, CTX.allChildren(), CTX.allChildrenWithFilter(filterOnFeatured), CTX.mapKey(Value.get("variants")), CTX.allChildrenWithFilter(filterOnVariantInventory) ));System.out.println(noFailResponse.getList("catalog"));# Operationops = [ operations.select_by_path( "catalog", [ cdt_ctx.cdt_ctx_all_children(), cdt_ctx.cdt_ctx_all_children_with_filter(filter_on_featured), cdt_ctx.cdt_ctx_map_key("variants"), cdt_ctx.cdt_ctx_all_children_with_filter(filter_on_variant_inventory) ], aerospike.EXP_PATH_SELECT_MAP_KEY | aerospike.EXP_PATH_SELECT_NO_FAIL)](key, meta, bins) = client.operate(key, ops)print(bins["catalog"])-
malformed_product.variantsis “no variant”. -
With
NO_FAIL,malformed_productis excluded silently because variants was “no variant”.
Expected output:
Same as the corresponding non-NO_FAIL query, omitting any items from malformed_product.
["2001","2003"]- ❌ Item
10000003skipped silently becausevariantswas a string.
Remove nested elements with modifyByPath
You can remove selected elements in place by combining a path filter with a removeResult expression.
The path navigates to and filters the target elements; removeResult expression tells the server to delete each match.
Example: Remove all size “M” variants across all products.
This example uses NO_FAIL because some products use list-backed variants or lack
a "size" key entirely. Without NO_FAIL, the operation would fail on those products.
// Remove each size M variant.session.update(key) .bin("catalog") .upsertFrom("$.catalog.inventory.*.variants.*[?(@.size == 'M')].remove():NO_FAIL") .execute();
Record readResult = session.query(key).execute().getFirstRecord();
System.out.println(readResult.getMap("catalog"));# Remove each size M variant.session.update(key).bin("catalog").upsert_from( "$.catalog.inventory.*.variants.*[?(@.size == 'M')].remove():NO_FAIL").execute()
record = session.query(key).first_or_raise().recordprint(record.bins["catalog"])use aerospike::expressions::{eq, exp_map_loop_var, exp_remove_result, maps as map_exp, string_val, ExpType, LoopVarPart};use aerospike::operations::cdt_context::{ctx_all_children, ctx_all_children_with_filter, ctx_map_key};use aerospike::operations::path::{self, ModifyFlag};use aerospike::{as_val, Bins, MapReturnType, ReadPolicy, WritePolicy};
let filter_on_size_m = eq( map_exp::get_by_key(MapReturnType::Value, ExpType::STRING, string_val("size".into()), exp_map_loop_var(LoopVarPart::VALUE), &[]), string_val("M".into()));
let remove_exp = exp_remove_result();
client.operate(&WritePolicy::default(), &key, &[ path::modify_by_path("catalog", ModifyFlag::NO_FAIL, remove_exp, [ ctx_all_children(), ctx_all_children(), ctx_map_key(as_val!("variants")), ctx_all_children_with_filter(filter_on_size_m), ]),]).await?;
let record = client.get(&ReadPolicy::default(), &key, Bins::All).await?;
println!("{}", record.bins["catalog"]);Exp filterOnSizeM = Exp.EQ( MapExp.GetByKey(MapReturnType.VALUE, Exp.Type.STRING, Exp.Val("size"), Exp.MapLoopVar(LoopVarPart.VALUE) ), Exp.Val("M"));
Expression removeExp = Exp.Build(Exp.RemoveResult());
client.Operate(null, key, CDTOperation.ModifyByPath("catalog", ModifyFlag.NO_FAIL, removeExp, CTX.AllChildren(), CTX.AllChildren(), CTX.MapKey(Value.Get("variants")), CTX.AllChildrenWithFilter(filterOnSizeM) ));
Record record = client.Get(null, key);Console.WriteLine(System.Text.Json.JsonSerializer.Serialize((Dictionary<object, object>)record.GetMap("catalog"), new System.Text.Json.JsonSerializerOptions { WriteIndented = true }));// Requires: import as "github.com/aerospike/aerospike-client-go/v8"filterOnSizeM := as.ExpEq( as.ExpMapGetByKey( as.MapReturnType.VALUE, as.ExpTypeSTRING, as.ExpStringVal("size"), as.ExpMapLoopVar(as.VALUE), ), as.ExpStringVal("M"),)
removeExp := as.ExpRemoveResult()
_, err = client.Operate(nil, key, as.ModifyByPath("catalog", as.EXP_PATH_MODIFY_NO_FAIL, removeExp, as.CtxAllChildren(), as.CtxAllChildren(), as.CtxMapKey(as.NewValue("variants")), as.CtxAllChildrenWithFilter(filterOnSizeM), ),)
record, _ := client.Get(nil, key)fmt.Println(record.Bins["catalog"])const filterOnSizeM = exp.eq( exp.maps.getByKey( exp.loopVarMap(exp.loopVarPart.VALUE), exp.str('size'), exp.type.STR, maps.returnType.VALUE ), exp.str('M'))
const removeExpression = exp.resultRemove()
const context = new Context()
context.addAllChildren()context.addAllChildren()context.addMapKey('variants')context.addAllChildrenWithFilter(filterOnSizeM)
const ops = [ op.modifyByPath("catalog", removeExpression, exp.pathModifyFlags.NO_FAIL, context)]
await client.operate(key, ops)
const result = await client.get(key)
console.log(result.bins.catalog)as_exp_build(filter_on_size_m, as_exp_cmp_eq( as_exp_map_get_by_key( NULL, AS_MAP_RETURN_VALUE, AS_EXP_TYPE_STR, as_exp_str("size"), as_exp_loopvar_map(AS_EXP_LOOPVAR_VALUE) ), as_exp_str("M")));
as_exp_build(remove_exp, as_exp_result_remove());
as_cdt_ctx ctx;as_cdt_ctx_inita(&ctx, 4);as_cdt_ctx_add_all_children(&ctx);as_cdt_ctx_add_all_children(&ctx);as_cdt_ctx_add_map_key(&ctx, (as_val*)as_string_new("variants", false));as_cdt_ctx_add_all_children_with_filter(&ctx, filter_on_size_m);
as_operations ops;as_operations_inita(&ops, 1);as_operations_modify_by_path(&err, &ops, "catalog", &ctx, remove_exp, AS_EXP_PATH_MODIFY_NO_FAIL);
as_record* rec = NULL;aerospike_key_operate(&as, &err, NULL, &key, &ops, &rec);
aerospike_key_get(&as, &err, NULL, &key, &rec);
as_map* map = as_record_get_map(rec, "catalog");if (map) { char* map_str = as_val_tostring((as_val*)map); printf("%s\n", map_str); free(map_str);} else { printf("No data returned\n");}Exp filterOnSizeM = Exp.eq( MapExp.getByKey(MapReturnType.VALUE, Exp.Type.STRING, Exp.val("size"), Exp.mapLoopVar(LoopVarPart.VALUE)), Exp.val("M"));
Expression removeExp = Exp.build(Exp.removeResult());
client.operate(null, key, CdtOperation.modifyByPath("catalog", ModifyFlags.NO_FAIL, removeExp, CTX.allChildren(), CTX.allChildren(), CTX.mapKey(Value.get("variants")), CTX.allChildrenWithFilter(filterOnSizeM) ));
Record record = client.get(null, key);
System.out.println(record.getMap("catalog"));filter_on_size_m = exp.Eq( exp.MapGetByKey( ctx=None, return_type=MAP_RETURN_VALUE, value_type=exp.ResultType.STRING, key=exp.Val("size"), bin=exp.LoopVarMap(aerospike.EXP_LOOPVAR_VALUE) ), exp.Val("M")).compile()
remove_exp = exp.RemoveResult().compile()
ops = [ operations.modify_by_path( "catalog", [ cdt_ctx.cdt_ctx_all_children(), cdt_ctx.cdt_ctx_all_children(), cdt_ctx.cdt_ctx_map_key("variants"), cdt_ctx.cdt_ctx_all_children_with_filter(filter_on_size_m) ], remove_exp, aerospike.EXP_PATH_MODIFY_NO_FAIL)]
client.operate(key, ops)
(key, meta, bins) = client.get(key)print(json.dumps(bins["catalog"], indent=4))Expected Record Modification (diff excerpt):
{ "inventory": { "10000001": { "variants": { "2001": { "size": "S", "price": 25, "quantity": 100 }, // "2002" (size M) removed "2003": { "size": "L", "price": 27, "quantity": 50 } } }, "10000002": { "variants": { // "2004" (size M) removed "2005": { "size": "XL", "price": 32, "quantity": 10 } } } }}- ✅ Variant
"2002"(size M) removed from Classic T-Shirt. - ✅ Variant
"2004"(size M) removed from Casual Polo Shirt. - ⚠️
NO_FAILlets the operation skip electronics products that lack a"size"key or use list-backed variants.
Handle errors gracefully
Path expression operations can fail for several reasons. Always wrap operations in error handling to provide a good user experience.
Common error scenarios:
- Target bin isn’t a CDT (a bin that doesn’t exist returns no value, not an error)
- Type mismatch in filter expression (without
NO_FAIL) - Server timeout during complex traversals
try { Record record = session.query(key) .bin("catalog") .selectFrom("$.catalog.inventory.*[?(@.featured == true)]" + ".variants.*[?(@.quantity > 0)].getTree()") .execute() .getFirstRecord(); // Success - process the filtered results System.out.println(record.getMap("catalog"));} catch (AerospikeException e) { switch (e.getResultCode()) { case ResultCode.BIN_TYPE_ERROR: case ResultCode.OP_NOT_APPLICABLE: // The bin exists but isn't a Map or List - can't traverse it System.err.println("Bin is not a Map or List"); break; case ResultCode.PARAMETER_ERROR: // The filter expression is malformed or uses wrong types System.err.println("Invalid expression in filter"); break; default: // Unexpected error (network, timeout, etc.) - propagate it throw e; }}from aerospike_sdk import ResultCodefrom aerospike_sdk.exceptions import AerospikeError, BinOpInvalidError, BinTypeError
try: result = ( session.query(key) .bin("catalog") .select_from( "$.catalog.inventory.*[?(@.featured == true)]" ".variants.*[?(@.quantity > 0)].getTree()" ) .first_or_raise() ) # Success - process the filtered results print(result.record.bins["catalog"])except (BinTypeError, BinOpInvalidError) as e: # The bin exists but isn't a Map or List - can't traverse it print(f"Bin is not a Map or List: {e}")except AerospikeError as e: if e.result_code == ResultCode.PARAMETER_ERROR: # The filter expression is malformed or uses wrong types print(f"Invalid expression in filter: {e}") else: # Unexpected error (network, timeout, etc.) - propagate it raiseuse aerospike::operations::cdt_context::{ctx_all_children, ctx_all_children_with_filter, ctx_map_key};use aerospike::operations::path::{self, SelectFlag};use aerospike::{as_val, ResultCode, WritePolicy};
// Execute the path expression operationmatch client.operate(&WritePolicy::default(), &key, &[ path::select_by_path("catalog", SelectFlag::MATCHING_TREE, [ ctx_all_children(), ctx_all_children_with_filter(filter_on_featured), ctx_map_key(as_val!("variants")), ctx_all_children_with_filter(filter_on_variant_inventory), ]),]).await { // Success - process the filtered results Ok(record) => println!("{}", record.bins["catalog"]), Err(e) => match e.server_result_code() { Some(ResultCode::BinTypeError) | Some(ResultCode::OpNotApplicable) => { // The bin exists but isn't a Map or List - can't traverse it eprintln!("Bin is not a Map or List"); } Some(ResultCode::ParameterError) => { // The filter expression is malformed or uses wrong types eprintln!("Invalid expression in filter"); } // Unexpected error (network, timeout, etc.) - propagate it _ => return Err(e), },}try{ Record record = client.Operate(null, key, CDTOperation.SelectByPath( "catalog", SelectFlag.MATCHING_TREE, CTX.AllChildren(), CTX.AllChildrenWithFilter(filterOnFeatured), CTX.MapKey(Value.Get("variants")), CTX.AllChildrenWithFilter(filterOnVariantInventory) ) );
// Success - process the filtered results Console.WriteLine(System.Text.Json.JsonSerializer.Serialize((Dictionary<object, object>)record.GetMap("catalog"), new System.Text.Json.JsonSerializerOptions { WriteIndented = true }));}catch (AerospikeException e){ switch (e.Result) { case ResultCode.BIN_TYPE_ERROR: case ResultCode.OP_NOT_APPLICABLE: // The bin exists but isn't a Map or List - can't traverse it Console.WriteLine("Bin is not a Map or List"); break; case ResultCode.PARAMETER_ERROR: // The filter expression is malformed or uses wrong types Console.WriteLine("Invalid expression in filter"); break;
default: // Unexpected error (network, timeout, etc.) - propagate it throw; }}// Requires: import as "github.com/aerospike/aerospike-client-go/v8"// Requires: import "github.com/aerospike/aerospike-client-go/v8/types"// Execute the path expression operation
record, err = client.Operate(nil, key, as.SelectByPath("catalog", as.EXP_PATH_SELECT_MATCHING_TREE, as.CtxAllChildren(), as.CtxAllChildrenWithFilter(filterOnFeatured), as.CtxMapKey(as.NewValue("variants")), as.CtxAllChildrenWithFilter(filterOnVariantInventory), ),)
// Check for errors and handle specific casesif err != nil { // Type assert to access Aerospike-specific error details if aeroErr, ok := err.(*as.AerospikeError); ok { switch aeroErr.ResultCode { case types.BIN_TYPE_ERROR, types.OP_NOT_APPLICABLE: // The bin exists but isn't a Map or List - can't traverse it log.Println("Bin is not a Map or List") case types.PARAMETER_ERROR: // The filter expression is malformed or uses wrong types log.Println("Invalid expression in filter") default: // Unexpected error (network, timeout, etc.) log.Fatal(aeroErr) } }} else { // Success - process the filtered results fmt.Println(record.Bins["catalog"])}const context = new Context()
context.addAllChildren()context.addAllChildrenWithFilter(filterOnFeatured)context.addMapKey('variants')context.addAllChildrenWithFilter(filterOnVariantInventory)
// Operationconst ops = [ op.selectByPath("catalog", exp.pathSelectFlags.MATCHING_TREE, context)]
try { // Execute the path expression operation const result = await client.operate(key, ops)
// Success - process the filtered results console.log(result.bins.catalog)
} catch (err) { if (err.code === Aerospike.status.ERR_BIN_INCOMPATIBLE_TYPE || err.code === Aerospike.status.ERR_OP_NOT_APPLICABLE) { // The bin exists but isn't a Map or List - can't traverse it console.error(`Bin is not a Map or List: ${err.message}`)
} else if (err.code === Aerospike.status.ERR_REQUEST_INVALID) { // The filter expression is malformed or uses wrong types console.error(`Invalid expression in filter: ${err.message}`)
} else { // Unexpected error (network, timeout, etc.) - propagate it throw err }}// filter_on_featured and filter_on_variant_inventory as in the quickstartas_cdt_ctx ctx;as_cdt_ctx_inita(&ctx, 4);as_cdt_ctx_add_all_children(&ctx);as_cdt_ctx_add_all_children_with_filter(&ctx, filter_on_featured);as_cdt_ctx_add_map_key(&ctx, (as_val*)as_string_new("variants", false));as_cdt_ctx_add_all_children_with_filter(&ctx, filter_on_variant_inventory);
as_operations ops;as_operations_inita(&ops, 1);as_operations_select_by_path(&err, &ops, "catalog", &ctx, AS_EXP_PATH_SELECT_MATCHING_TREE);
as_record* rec = NULL;as_status status = aerospike_key_operate(&as, &err, NULL, &key, &ops, &rec);if (status != AEROSPIKE_OK) { switch (status) { case AEROSPIKE_ERR_BIN_INCOMPATIBLE_TYPE: case AEROSPIKE_ERR_OP_NOT_APPLICABLE: // The bin exists but isn't a Map or List; cannot traverse it. fprintf(stderr, "Bin is not a Map or List\n"); break; case AEROSPIKE_ERR_REQUEST_INVALID: // The filter expression is malformed or uses wrong types fprintf(stderr, "Invalid expression in filter\n"); break; default: fprintf(stderr, "Error: code %d, message: %s\n", err.code, err.message ); break; }}else { // Success - process the filtered results as_map* map = as_record_get_map(rec, "catalog"); if (map) { char* map_str = as_val_tostring((as_val*)map); printf("%s\n", map_str); free(map_str); } else { printf("No data returned\n"); }}import com.aerospike.client.AerospikeException;import com.aerospike.client.ResultCode;
try { Record record = client.operate(null, key, CdtOperation.selectByPath("catalog", SelectFlags.MATCHING_TREE, CTX.allChildren(), CTX.allChildrenWithFilter(filterOnFeatured), CTX.mapKey(Value.get("variants")), CTX.allChildrenWithFilter(filterOnVariantInventory))); // Success - process the filtered results System.out.println(record.getMap("catalog"));} catch (AerospikeException e) { switch (e.getResultCode()) { case ResultCode.BIN_TYPE_ERROR: case ResultCode.OP_NOT_APPLICABLE: // The bin exists but isn't a Map or List - can't traverse it System.err.println("Bin is not a Map or List"); break; case ResultCode.PARAMETER_ERROR: // The filter expression is malformed or uses wrong types System.err.println("Invalid expression in filter"); break; default: // Unexpected error (network, timeout, etc.) - propagate it throw e; }}import aerospikefrom aerospike import exception as aero_exception
ops = [ operations.select_by_path( "catalog", [ cdt_ctx.cdt_ctx_all_children(), cdt_ctx.cdt_ctx_all_children_with_filter(filter_on_featured), cdt_ctx.cdt_ctx_map_key('variants'), cdt_ctx.cdt_ctx_all_children_with_filter(filter_on_variant_inventory) ], aerospike.EXP_PATH_SELECT_MATCHING_TREE)]try: # Execute the path expression operation (key, meta, bins) = client.operate(key, ops) # Success - process the filtered results print(bins["catalog"])
except aero_exception.OpNotApplicable as e: # The bin exists but isn't a Map or List - can't traverse it print(f"Bin is not a Map or List: {e}")
except aero_exception.BinIncompatibleType as e: # The bin exists but isn't a Map or List - can't traverse it print(f"Bin is not a Map or List: {e}")
except aero_exception.InvalidRequest as e: # The filter expression is malformed or uses wrong types print(f"Invalid expression in filter: {e}")
except aero_exception.AerospikeError as e: # Unexpected error (network, timeout, etc.) - propagate it raise