Path expressions usage examples
This page contains examples of using path expressions to select elements from a document modeled in Aerospike as nested map and list structures.
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.
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))filterOnKey := aero.ExpRegexCompare("1000.*", aero.ExpRegexFlagNONE, aero.ExpStringLoopVar(aero.MAP_KEY))// Operationrecord, err := client.Operate(nil, key, aero.SelectByPath("catalog", aero.EXP_PATH_SELECT_MATCHING_TREE, aero.CtxAllChildren(), aero.CtxAllChildrenWithFilter(filterOnKey), ),)
fmt.Println(record.Bins["catalog"])// Filteras_exp_build(filter_on_key, as_exp_cmp_regex( 0, "10000.*", as_exp_loopvar_str(AS_EXP_LOOPVAR_KEY)));
if (!filter_on_key) { goto fail_filter_on_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);status = as_operations_select_by_path(&err, &ops, "catalog", &ctx, AS_EXP_PATH_SELECT_MATCHING_TREE);if (status != AEROSPIKE_OK) { goto fail_select_by_path;}
as_record* rec = NULL;status = aerospike_key_operate(&as, &err, NULL, &key, &ops, &rec);if (status != AEROSPIKE_OK) { goto fail_key_operate;}
// Print in JSON format
as_map* map = as_record_get_map(rec, "catalog");if (map) { char* map_str = as_val_tostring((as_val*)map); log("%s\n", map_str); free(map_str);} else { log("No data returned\n");}Exp filterOnKey = Exp.RegexCompare("10000.*", 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 }));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)Expected output:
{ "inventory": { "10000001": { ... }, "10000002": { ... } }}- ✅ Only products whose keys start with
10000are 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.
// 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"])// OperationreadResult, err := client.Operate(nil, key, aero.SelectByPath("catalog", aero.EXP_PATH_SELECT_MAP_KEY | aero.EXP_PATH_SELECT_NO_FAIL, aero.CtxAllChildren(), aero.CtxAllChildrenWithFilter(filterOnFeatured), aero.CtxMapKey(aero.NewValue("variants")), aero.CtxAllChildrenWithFilter(filterOnVariantInventory), ),)fmt.Println(readResult.Bins["catalog"])// Details elided for brevityas_exp_build(filter_on_featured, ...);as_exp_build(filter_on_variant_inventory, ...);
// 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);status = as_operations_select_by_path(&err, &ops, "catalog", &ctx, AS_EXP_PATH_SELECT_MAP_KEY | AS_EXP_PATH_SELECT_NO_FAIL);if (status != AEROSPIKE_OK) { goto fail_select_by_path;}
as_record* rec = NULL;status = aerospike_key_operate(&as, &err, NULL, &key, &ops, &rec);if (status != AEROSPIKE_OK) { goto fail_key_operate;}
as_list* list = as_record_get_list(rec, "catalog");if (list) { char* list_str = as_val_tostring((as_val*)list); log("%s\n", list_str); free(list_str);} else { log("No data returned\n");}// 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 }));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)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 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))// Increment quantity by 10 and return the modified mapincrementQuantity := aero.ExpMapPut( aero.DefaultMapPolicy(), aero.ExpStringVal("quantity"), aero.ExpNumAdd( aero.ExpMapGetByKey( aero.MapReturnType.VALUE, aero.ExpTypeINT, aero.ExpStringVal("quantity"), aero.ExpMapLoopVar(aero.VALUE), ), aero.ExpIntVal(10), ), aero.ExpMapLoopVar(aero.VALUE),)
_, err = client.Operate(nil, key, aero.ModifyByPath("catalog", aero.EXP_PATH_MODIFY_DEFAULT, incrementQuantity, aero.CtxAllChildren(), aero.CtxAllChildrenWithFilter(filterOnFeatured), aero.CtxMapKey(aero.NewValue("variants")), aero.CtxAllChildrenWithFilter(filterOnVariantInventory), ),)
record, _ := client.Get(nil, key)fmt.Println(record.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)));if (increment_quantity == NULL) { goto fail_increment_quantity;}
// Details elided for brevityas_exp_build(filter_on_featured, ...);as_exp_build(filter_on_variant_inventory, ...);
// 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);status = as_operations_modify_by_path(&err, &ops, "catalog", &ctx, increment_quantity, AS_EXP_PATH_MODIFY_DEFAULT);if (status != AEROSPIKE_OK) { goto fail_modify_by_path;}
as_record* rec = NULL;status = aerospike_key_operate(&as, &err, NULL, &key, &ops, &rec);if (status != AEROSPIKE_OK) { goto fail_key_operate;}
status = aerospike_key_get(&as, &err, NULL, &key, &rec);if (status != AEROSPIKE_OK) { goto fail_key_get;}
as_map* map = as_record_get_map(rec, "catalog");if (map) { char* map_str = as_val_tostring((as_val*)map); log("%s\n", map_str); free(map_str);} else { log("No data returned\n");}// 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 }));// 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)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.
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.ResultRemove().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)// Match variants where quantity == 0.outOfStockFilter := aero.ExpEq( aero.ExpMapGetByKey( aero.MapReturnType.VALUE, aero.ExpTypeINT, aero.ExpStringVal("quantity"), aero.ExpMapLoopVar(aero.VALUE), ), aero.ExpIntVal(0),)
// Remove each matched element.removeMatched := aero.ExpRemoveResult()
_, err = client.Operate(nil, key, aero.ModifyByPath("catalog", aero.EXP_PATH_MODIFY_NO_FAIL, removeMatched, aero.CtxAllChildren(), aero.CtxAllChildrenWithFilter(filterOnFeatured), aero.CtxMapKey(aero.NewValue("variants")), aero.CtxAllChildrenWithFilter(outOfStockFilter), ),)// 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_MAP_VALUE) ), as_exp_int64(0) ));if (! out_of_stock_filter) { goto fail_out_of_stock_filter;}
// Remove each matched element.as_exp_build(remove_matched, as_exp_result_remove());if (! remove_matched) { goto fail_remove_matched;}
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);status = as_operations_modify_by_path( &err, &ops, "catalog", &ctx, remove_matched, AS_EXP_PATH_MODIFY_NO_FAIL);// 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.RemoveResults());
client.Operate(null, key, CDTOperation.ModifyByPath("catalog", ModifyFlag.NO_FAIL, removeMatched, CTX.AllChildren(), CTX.AllChildrenWithFilter(filterOnFeatured), CTX.MapKey(Value.Get("variants")), CTX.AllChildrenWithFilter(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)Expected record modification:
{ "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.
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"])filterOnCheapInStock := aero.ExpAnd( aero.ExpGreater( aero.ExpMapGetByKey( aero.MapReturnType.VALUE, aero.ExpTypeINT, aero.ExpStringVal("quantity"), aero.ExpMapLoopVar(aero.VALUE), ), aero.ExpIntVal(0), ), aero.ExpLess( aero.ExpMapGetByKey( aero.MapReturnType.VALUE, aero.ExpTypeINT, aero.ExpStringVal("price"), aero.ExpMapLoopVar(aero.VALUE), ), aero.ExpIntVal(50), ),)// OperationcheapInStock, err := client.Operate(nil, key, aero.SelectByPath("catalog", aero.EXP_PATH_SELECT_MATCHING_TREE, aero.CtxAllChildren(), aero.CtxAllChildrenWithFilter(filterOnFeatured), aero.CtxMapKey(aero.NewValue("variants")), aero.CtxAllChildrenWithFilter(filterOnCheapInStock), ),)fmt.Println(cheapInStock.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) )));if (filter_on_cheap_in_stock == NULL) { goto fail_filter_on_cheap_in_stock;}
// 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);status = as_operations_select_by_path(&err, &ops, "catalog", &ctx, AS_EXP_PATH_SELECT_MATCHING_TREE);if (status != AEROSPIKE_OK) { goto fail_select_by_path;}
as_record* rec = NULL;status = aerospike_key_operate(&as, &err, NULL, &key, &ops, &rec);if (status != AEROSPIKE_OK) { goto fail_key_operate;}
// Print in JSON format
as_map* map = as_record_get_map(rec, "catalog");if (map) { char* map_str = as_val_tostring((as_val*)map); log("%s\n", map_str); free(map_str);} else { log("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", 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 }));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)Expected output:
{ "inventory": { "10000001": { "variants": { "2001": { "price": 25, "quantity": 100, "size": "S" }, "2003": { "price": 27, "quantity": 50, "size": "L" } } }, "50000006": { "variants": {} }, "50000009": { "variants": {} } }}- ✅ Only items are returned which have price < 50 and quantity > 0.
- ❌ Item
10000002, Casual Polo Shirt, excluded (featured=false). - ❌ Item
50000006, Laptop Pro 14, excluded (quantity=0). - ❌ 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.
// 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"])// Operationrecord, _ := client.Operate(nil, key, aero.SelectByPath("catalog", aero.EXP_PATH_SELECT_MAP_KEY | aero.EXP_PATH_SELECT_NO_FAIL, aero.CtxAllChildren(), aero.CtxAllChildrenWithFilter(filterOnFeatured), aero.CtxMapKey(aero.NewValue("variants")), aero.CtxAllChildrenWithFilter(filterOnVariantInventory), ),)fmt.Println(record.Bins["catalog"])as_exp_build(filter_on_featured, ...);as_exp_build(filter_on_variant_inventory, ...);
// 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);status = as_operations_select_by_path(&err, &ops, "catalog", &ctx, AS_EXP_PATH_SELECT_MAP_KEY | AS_EXP_PATH_SELECT_NO_FAIL);if (status != AEROSPIKE_OK) { goto fail_select_by_path;}
as_record* rec = NULL;status = aerospike_key_operate(&as, &err, NULL, &key, &ops, &rec);if (status != AEROSPIKE_OK) { goto fail_key_operate;}
as_list* list = as_record_get_list(rec, "catalog");if (list) { char* list_str = as_val_tostring((as_val*)list); log("%s\n", list_str); free(list_str);} else { log("No data returned\n");}// 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 }));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)-
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.
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.ResultRemove().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))filterOnSizeM := aero.ExpEq( aero.ExpMapGetByKey( aero.MapReturnType.VALUE, aero.ExpTypeSTRING, aero.ExpStringVal("size"), aero.ExpMapLoopVar(aero.VALUE), ), aero.ExpStringVal("M"),)
removeExp := aero.ExpRemoveResult()
_, err = client.Operate(nil, key, aero.ModifyByPath("catalog", aero.EXP_PATH_MODIFY_NO_FAIL, removeExp, aero.CtxAllChildren(), aero.CtxAllChildren(), aero.CtxMapKey(aero.NewValue("variants")), aero.CtxAllChildrenWithFilter(filterOnSizeM), ),)
record, _ := client.Get(nil, key)fmt.Println(record.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")));if (filter_on_size_m == NULL) { goto fail_filter_on_size_m;}
as_exp_build(remove_exp, as_exp_result_remove());if (remove_exp == NULL) { goto fail_remove_exp;}
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);status = as_operations_modify_by_path(&err, &ops, "catalog", &ctx, remove_exp, AS_EXP_PATH_MODIFY_NO_FAIL);if (status != AEROSPIKE_OK) { goto fail_modify_by_path;}
as_record* rec = NULL;status = aerospike_key_operate(&as, &err, NULL, &key, &ops, &rec);if (status != AEROSPIKE_OK) { goto fail_key_operate;}
status = aerospike_key_get(&as, &err, NULL, &key, &rec);if (status != AEROSPIKE_OK) { goto fail_key_get;}
as_map* map = as_record_get_map(rec, "catalog");if (map) { char* map_str = as_val_tostring((as_val*)map); log("%s\n", map_str); free(map_str);} else { log("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.RemoveResults());
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 }));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)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 doesn’t exist or isn’t a CDT
- Type mismatch in filter expression (without
NO_FAIL) - Server timeout during complex traversals
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// Execute the path expression operation
record, err = client.Operate(nil, key, aero.SelectByPath("catalog", aero.EXP_PATH_SELECT_MATCHING_TREE, aero.CtxAllChildren(), aero.CtxAllChildrenWithFilter(filterOnFeatured), aero.CtxMapKey(aero.NewValue("variants")), aero.CtxAllChildrenWithFilter(filterOnVariantInventory), ),)
// Check for errors and handle specific casesif err != nil { // Type assert to access Aerospike-specific error details if aeroErr, ok := err.(*aero.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) } }}
// Success - process the filtered resultsfmt.Println(record.Bins["catalog"])// ... setup elided for brevity ...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);status = as_operations_select_by_path(&err, &ops, "catalog", &ctx, AS_EXP_PATH_SELECT_MATCHING_TREE);if (status != AEROSPIKE_OK) { goto fail_select_by_path;}
as_record* rec = NULL;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_PARAM: // 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); log("%s\n", map_str); free(map_str); } else { log("No data returned\n"); }}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; }}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 }}