Working with nested collection data types
This page demonstrates how to write, read, filter, index, and query data within nested List and Map structures. The examples build on each other, using three complementary techniques:
- CDT operate API with context selectors for single-element writes and reads at known positions.
- Expression composition (nesting
ListExp/MapExpcalls) for reading or filtering nested values within record-level expressions. - Path expressions for selecting or filtering across multiple nested elements at once.
The examples on this page use a record with a bin named vehicles and a
string bin named username. The vehicles bin
contains a List of Maps, where each Map represents a vehicle with color,
license, make, and model fields. The map keys are shown in key order
(K-order), which is how the application stores them.
[ { "color": "white", "license": "8PAJ017", "make": "Toyota", "model": "RAV4" }, { "color": "blue", "license": "6ABC123", "make": "Tesla", "model": "Model 3" }, { "color": "silver", "license": "7XYZ789", "make": "Honda", "model": "Civic" }]We can visualize the data another way:
List├── [0] Map│ ├── "color" -> "white"│ ├── "license" -> "8PAJ017"│ ├── "make" -> "Toyota"│ └── "model" -> "RAV4"├── [1] Map│ ├── "color" -> "blue"│ ├── "license" -> "6ABC123"│ ├── "make" -> "Tesla"│ └── "model" -> "Model 3"└── [2] Map ├── "color" -> "silver" ├── "license" -> "7XYZ789" ├── "make" -> "Honda" └── "model" -> "Civic"Why the list is unordered and the maps are key-ordered
This data model makes two distinct ordering choices, each for a specific reason.
The list is unordered. Index 0 means “default vehicle” — the application assigns semantic meaning to position. An ordered list would sort the vehicles by their map comparison value, which would rearrange the entries and break the positional contract. You keep the list unordered so that index 0 stays the default, index 1 is the second choice, and so on.
The maps are key-ordered. In this example the maps use the K-ordered subtype, which stores elements in key order. You construct the maps with keys in K-order on the client side for two reasons:
- Correct
ADD_UNIQUEcomparison. When you use theADD_UNIQUEwrite flag to prevent duplicate vehicles in the list, the server compares map elements by element count, then keys in stored order, then values. The incoming map must already be K-ordered for the comparison to match the stored form. If a client sends the same vehicle data with keys in a different order, the wire representation differs, comparison fails, and the duplicate slips through. Only ordered maps (K-ordered or KV-ordered) can be reliably compared for equality. - Predictable read results. When you read the data back, map entries come back in key order. If the application constructs maps with keys in an arbitrary order, the displayed result differs from what was sent, which makes debugging harder.
How you construct a K-ordered map depends on the client language:
- Java — use
TreeMap(sorts keys by natural order). - Python — use
aerospike.KeyOrderedDict. It maps toas_orderedmapin the C layer, which sorts keys and includes the K-ordered flag on the wire. A standarddictmaps toas_hashmap, which sorts keys internally but omits the K-ordered wire flag, so the server treats it as unordered. - C — use
as_orderedmap(as the example does). It keeps keys sorted and includes the K-ordered flag on the wire.as_hashmapalso sorts internally but omits the K-ordered wire flag. - Go — use a
[]as.MapPairslice with keys in sorted order. - C# — use
SortedDictionary<string, object>. - Node.js — use a plain object or
Map. The Node.js binding creates anas_orderedmapinternally, which sorts keys and includes the K-ordered wire flag. Key order in the JavaScript source does not matter.
The vehicle maps in this example are small (four keys each), so the
per-operation index rebuild cost is negligible. For maps with many keys,
consider using
PERSIST_INDEX
to store the offset index on disk. See
Map performance for
operational complexity by subtype and index configuration.
Add a new vehicle as the default
The following example inserts a new vehicle at index 0, making it the default. The list policy uses three flags:
UNORDERED— the list is unordered so that index 0 keeps its “default vehicle” meaning.ADD_UNIQUE— prevents inserting a vehicle that already exists in the list, compared as a full map value.NO_FAIL— the operation succeeds silently if the vehicle is already present, so the caller does not need to handle a duplicate error.
TreeMap<String, Object> vehicle = new TreeMap<>();vehicle.put("color", "red");vehicle.put("license", "5DEF456");vehicle.put("make", "Ford");vehicle.put("model", "Mustang");
ListPolicy policy = new ListPolicy(ListOrder.UNORDERED, ListWriteFlags.ADD_UNIQUE | ListWriteFlags.NO_FAIL);
Record record = client.operate(null, key, ListOperation.insert(policy, "vehicles", 0, Value.get(vehicle)), Operation.get("vehicles"));from aerospike import KeyOrderedDictfrom aerospike_helpers.operations import list_operations
vehicle = KeyOrderedDict({ "color": "red", "license": "5DEF456", "make": "Ford", "model": "Mustang"})
policy = { "list_order": aerospike.LIST_UNORDERED, "write_flags": aerospike.LIST_WRITE_ADD_UNIQUE | aerospike.LIST_WRITE_NO_FAIL}
ops = [ list_operations.list_insert("vehicles", 0, vehicle, policy)](key, meta, bins) = client.operate(key, ops)vehicle := []as.MapPair{ {Key: "color", Value: "red"}, {Key: "license", Value: "5DEF456"}, {Key: "make", Value: "Ford"}, {Key: "model", Value: "Mustang"},}
policy := as.NewListPolicy(as.ListOrderUnordered, as.ListWriteFlagsAddUnique|as.ListWriteFlagsNoFail)
record, err := client.Operate(nil, key, as.ListInsertWithPolicyOp(policy, "vehicles", 0, vehicle), as.GetBinOp("vehicles"),)as_orderedmap vehicle;as_orderedmap_init(&vehicle, 4);as_orderedmap_set(&vehicle, (as_val*)as_string_new("color", false), (as_val*)as_string_new("red", false));as_orderedmap_set(&vehicle, (as_val*)as_string_new("license", false), (as_val*)as_string_new("5DEF456", false));as_orderedmap_set(&vehicle, (as_val*)as_string_new("make", false), (as_val*)as_string_new("Ford", false));as_orderedmap_set(&vehicle, (as_val*)as_string_new("model", false), (as_val*)as_string_new("Mustang", false));
as_list_policy policy;as_list_policy_set(&policy, AS_LIST_UNORDERED, AS_LIST_WRITE_ADD_UNIQUE | AS_LIST_WRITE_NO_FAIL);
as_operations ops;as_operations_inita(&ops, 2);as_operations_list_insert(&ops, "vehicles", NULL, &policy, 0, (as_val*)&vehicle);as_operations_add_read(&ops, "vehicles");
as_record* rec = NULL;aerospike_key_operate(&as, &err, NULL, &key, &ops, &rec);var vehicle = new SortedDictionary<string, object>{ { "color", "red" }, { "license", "5DEF456" }, { "make", "Ford" }, { "model", "Mustang" }};
ListPolicy policy = new ListPolicy(ListOrder.UNORDERED, ListWriteFlags.ADD_UNIQUE | ListWriteFlags.NO_FAIL);
Record record = client.Operate(null, key, ListOperation.Insert(policy, "vehicles", 0, Value.Get(vehicle)), Operation.Get("vehicles"));const vehicle = { color: 'red', license: '5DEF456', make: 'Ford', model: 'Mustang'}
const policy = new Aerospike.ListPolicy({ order: Aerospike.lists.order.UNORDERED, writeFlags: Aerospike.lists.writeFlags.ADD_UNIQUE | Aerospike.lists.writeFlags.NO_FAIL})
const result = await client.operate(key, [ Aerospike.lists.insert('vehicles', 0, vehicle, policy), Aerospike.operations.read('vehicles')])After this operation the list contains four vehicles, with the Ford Mustang
at index 0 (the new default). Running the same operation again has no effect
because ADD_UNIQUE detects the duplicate and NO_FAIL suppresses the error.
Read the license plate of the default vehicle
The first vehicle in the list (index 0) acts as the default vehicle.
The following expression read extracts its license plate by nesting
ListExp.getByIndex and MapExp.getByKey:
ListExp.getByIndexextracts the map at index 0 from thevehiclesbin.MapExp.getByKeyextracts the"license"value from that map.ExpOperation.readreturns the result under the name"defaultLicense".
Record record = client.operate(null, key, ExpOperation.read("defaultLicense", Exp.build( MapExp.getByKey(MapReturnType.VALUE, Exp.Type.STRING, Exp.val("license"), ListExp.getByIndex(ListReturnType.VALUE, Exp.Type.MAP, Exp.val(0), Exp.listBin("vehicles")))), ExpReadFlags.DEFAULT));
String plate = record.getString("defaultLicense");from aerospike_helpers import expressions as expfrom aerospike_helpers.operations import expression_operations as exp_ops
read_exp = exp.MapGetByKey( ctx=None, return_type=aerospike.MAP_RETURN_VALUE, value_type=exp.ResultType.STRING, key=exp.Val("license"), bin=exp.ListGetByIndex( ctx=None, return_type=aerospike.LIST_RETURN_VALUE, value_type=exp.ResultType.MAP, index=exp.Val(0), bin=exp.ListBin("vehicles") )).compile()
ops = [exp_ops.expression_read("defaultLicense", read_exp)](key, meta, bins) = client.operate(key, ops)print(bins["defaultLicense"])readExp := aero.ExpMapGetByKey( aero.MapReturnType.VALUE, aero.ExpTypeSTRING, aero.ExpStringVal("license"), aero.ExpListGetByIndex( aero.ListReturnTypeValue, aero.ExpTypeMAP, aero.ExpIntVal(0), aero.ExpListBin("vehicles"), ),)
record, err := client.Operate(nil, key, aero.ExpReadOp("defaultLicense", readExp, aero.ExpReadFlagDefault),)
fmt.Println(record.Bins["defaultLicense"])as_exp_build(read_exp, as_exp_map_get_by_key(NULL, AS_MAP_RETURN_VALUE, AS_EXP_TYPE_STR, as_exp_str("license"), as_exp_list_get_by_index(NULL, AS_LIST_RETURN_VALUE, AS_EXP_TYPE_MAP, as_exp_int(0), as_exp_bin_list("vehicles"))));
as_operations ops;as_operations_inita(&ops, 1);as_operations_exp_read(&ops, "defaultLicense", read_exp, AS_EXP_READ_DEFAULT);
as_record* rec = NULL;aerospike_key_operate(&as, &err, NULL, &key, &ops, &rec);
char* plate = as_record_get_str(rec, "defaultLicense");Record record = client.Operate(null, key, ExpOperation.Read("defaultLicense", Exp.Build( MapExp.GetByKey(MapReturnType.VALUE, Exp.Type.STRING, Exp.Val("license"), ListExp.GetByIndex(ListReturnType.VALUE, Exp.Type.MAP, Exp.Val(0), Exp.ListBin("vehicles")))), ExpReadFlags.DEFAULT));
string plate = record.GetString("defaultLicense");const readExp = exp.maps.getByKey( exp.lists.getByIndex( exp.binList('vehicles'), exp.int(0), exp.type.MAP, lists.returnType.VALUE ), exp.str('license'), exp.type.STR, maps.returnType.VALUE)
const result = await client.operate(key, [ exp.operations.read('defaultLicense', readExp, exp.expReadFlags.DEFAULT)])
console.log(result.bins.defaultLicense)Expected output: "8PAJ017"
Check a license plate against all vehicles
The previous example accessed a single vehicle at a known index. To check a value across all vehicles regardless of list size, you can use a path expression to extract values from every element, then test the resulting list.
The following filter expression checks whether any vehicle in the list has
a license plate matching "7XYZ789". A
path expression extracts all "license" values
regardless of list size, then ListExp.getByValue with EXISTS checks
whether the target plate is present. This filter expression can be used for record selection with
a query, similar to a relational database’s “WHERE” clause.
String targetPlate = "7XYZ789";
QueryPolicy policy = client.copyQueryPolicyDefault();policy.filterExp = Exp.build( ListExp.getByValue(ListReturnType.EXISTS, Exp.val(targetPlate), CdtExp.selectByPath(Exp.Type.LIST, SelectFlags.VALUE, Exp.listBin("vehicles"), CTX.allChildren(), CTX.mapKey(Value.get("license")))));from aerospike_helpers import expressions as expfrom aerospike_helpers.expressions.base import SelectByPath, ListBin, ResultTypefrom aerospike_helpers import cdt_ctx
target_plate = "7XYZ789"
all_licenses = SelectByPath( ctx=[ cdt_ctx.cdt_ctx_all_children(), cdt_ctx.cdt_ctx_map_key("license") ], value_type=ResultType.LIST, flags=aerospike.EXP_PATH_SELECT_VALUE, bin=ListBin("vehicles"))
query.policy = { "expressions": exp.ListGetByValue( ctx=None, return_type=aerospike.LIST_RETURN_EXISTS, value=exp.Val(target_plate), bin=all_licenses ).compile()}targetPlate := "7XYZ789"
allLicenses := aero.ExpSelectByPath( aero.ExpTypeLIST, aero.EXP_PATH_SELECT_VALUE, aero.ExpListBin("vehicles"), aero.CtxAllChildren(), aero.CtxMapKey(aero.NewValue("license")),)
queryPolicy := aero.NewQueryPolicy()queryPolicy.FilterExpression = aero.ExpListGetByValue( aero.ListReturnTypeExists, aero.ExpStringVal(targetPlate), allLicenses,)as_cdt_ctx ctx;as_cdt_ctx_inita(&ctx, 2);as_cdt_ctx_add_all_children(&ctx);as_cdt_ctx_add_map_key(&ctx, (as_val*)as_string_new("license", false));
as_exp_build(filter, as_exp_list_get_by_value(NULL, AS_LIST_RETURN_EXISTS, as_exp_str("7XYZ789"), as_exp_select_by_path(&ctx, AS_EXP_TYPE_LIST, AS_EXP_PATH_SELECT_VALUE, as_exp_bin_list("vehicles"))));
as_policy_query policy;as_policy_query_init(&policy);policy.base.filter_exp = filter;string targetPlate = "7XYZ789";
QueryPolicy policy = new QueryPolicy(client.QueryPolicyDefault);policy.filterExp = Exp.Build( ListExp.GetByValue(ListReturnType.EXISTS, Exp.Val(targetPlate), CDTExp.SelectByPath(Exp.Type.LIST, SelectFlag.VALUE, Exp.ListBin("vehicles"), CTX.AllChildren(), CTX.MapKey(Value.Get("license")))));const targetPlate = '7XYZ789'
const ctx = new Context() .addAllChildren() .addMapKey('license')
const allLicenses = exp.selectByPath( exp.binList('vehicles'), exp.type.LIST, exp.pathSelectFlags.VALUE, ctx)
const policy = { filterExpression: exp.lists.getByValue( allLicenses, exp.str(targetPlate), lists.returnType.EXISTS )}Only records where at least one vehicle has a matching license plate pass this filter.
Create an expression index on license plates
The filter expression in the previous section evaluates against every record during a query. Instead of scanning every record, you can create a secondary index on the license plate values. A secondary index on the extracted values lets the server skip non-matching records entirely, which is significantly faster for large datasets.
A path expression extracts all "license" values
into a list, and the index is created with collection type LIST so each
license string is indexed individually.
Step 1: Create the index
The expression uses selectByPath to walk every element in the vehicles list
and extract the "license" value from each map.
Expression licensesExp = Exp.build( CdtExp.selectByPath(Exp.Type.LIST, SelectFlags.VALUE, Exp.listBin("vehicles"), CTX.allChildren(), CTX.mapKey(Value.get("license"))));
IndexTask task = client.createIndex(null, "test", "demo", "idx_vehicle_license", IndexType.STRING, IndexCollectionType.LIST, licensesExp);task.waitTillComplete();from aerospike_helpers import cdt_ctxfrom aerospike_helpers.expressions.base import SelectByPath, ListBin, ResultType
licenses_exp = SelectByPath( ctx=[ cdt_ctx.cdt_ctx_all_children(), cdt_ctx.cdt_ctx_map_key("license") ], value_type=ResultType.LIST, flags=aerospike.EXP_PATH_SELECT_VALUE, bin=ListBin("vehicles")).compile()
client.index_expr_create("test", "demo", aerospike.INDEX_TYPE_LIST, aerospike.INDEX_STRING, licenses_exp, "idx_vehicle_license")licensesExp := aero.ExpSelectByPath( aero.ExpTypeLIST, aero.EXP_PATH_SELECT_VALUE, aero.ExpListBin("vehicles"), aero.CtxAllChildren(), aero.CtxMapKey(aero.NewValue("license")),)
task, err := client.CreateIndexWithExpression(nil, "test", "demo", "idx_vehicle_license", aero.STRING, aero.ICT_LIST, licensesExp)task.OnComplete()as_cdt_ctx ctx;as_cdt_ctx_inita(&ctx, 2);as_cdt_ctx_add_all_children(&ctx);as_cdt_ctx_add_map_key(&ctx, (as_val*)as_string_new("license", false));
as_exp_build(licenses_exp, as_exp_select_by_path(&ctx, AS_EXP_TYPE_LIST, AS_EXP_PATH_SELECT_VALUE, as_exp_bin_list("vehicles")));
as_index_task task;aerospike_index_create_exp(&as, &err, &task, NULL, "test", "demo", "idx_vehicle_license", AS_INDEX_TYPE_LIST, AS_INDEX_STRING, licenses_exp);
aerospike_index_create_wait(&err, &task, 0);Expression licensesExp = Exp.Build( CDTExp.SelectByPath(Exp.Type.LIST, SelectFlag.VALUE, Exp.ListBin("vehicles"), CTX.AllChildren(), CTX.MapKey(Value.Get("license"))));
IndexTask task = client.CreateIndex(null, "test", "demo", "idx_vehicle_license", IndexType.STRING, IndexCollectionType.LIST, licensesExp);task.Wait();const ctx = new Context() .addAllChildren() .addMapKey('license')
const licensesExp = exp.selectByPath( exp.binList('vehicles'), exp.type.LIST, exp.pathSelectFlags.VALUE, ctx)
await client.createExpIndex({ ns: 'test', set: 'demo', index: 'idx_vehicle_license', exp: licensesExp, type: Aerospike.indexType.LIST, datatype: Aerospike.indexDataType.STRING})Step 2: Query the index
Once the index exists, query for records that contain a specific license plate. Because the index is built on an expression (not a bin), the query filter references the same expression.
Instead of returning the full record, this query uses
operation projection to return only
the matching vehicle and the username bin. A selectByPath operation
with allChildrenWithFilter extracts the vehicle whose license matches
"7XYZ789", and a plain bin read returns the username.
Exp matchPlate = Exp.eq( MapExp.getByKey(MapReturnType.VALUE, Exp.Type.STRING, Exp.val("license"), Exp.mapLoopVar(LoopVarPart.VALUE)), Exp.val("7XYZ789"));
Statement stmt = new Statement();stmt.setNamespace("test");stmt.setSetName("demo");stmt.setFilter(Filter.contains(licensesExp, IndexCollectionType.LIST, "7XYZ789"));stmt.setOperations(new Operation[] { CdtOperation.selectByPath("vehicles", SelectFlags.VALUE, CTX.allChildrenWithFilter(matchPlate)), Operation.get("username")});
RecordSet rs = client.query(null, stmt);from aerospike import predicates as pfrom aerospike_helpers.operations import operations
match_plate = exp.Eq( exp.MapGetByKey( ctx=None, return_type=aerospike.MAP_RETURN_VALUE, value_type=exp.ResultType.STRING, key=exp.Val("license"), bin=exp.LoopVarMap(aerospike.EXP_LOOPVAR_VALUE) ), exp.Val("7XYZ789")).compile()
query = client.query("test", "demo")query.where_with_expr(licenses_exp, p.contains(None, aerospike.INDEX_TYPE_LIST, "7XYZ789"))query.add_ops([ operations.select_by_path( bin_name="vehicles", ctx=[cdt_ctx.cdt_ctx_all_children_with_filter(match_plate)], flags=aerospike.EXP_PATH_SELECT_VALUE), operations.read("username")])
results = query.results()matchPlate := aero.ExpEq( aero.ExpMapGetByKey( aero.MapReturnType.VALUE, aero.ExpTypeSTRING, aero.ExpStringVal("license"), aero.ExpMapLoopVar(aero.VALUE), ), aero.ExpStringVal("7XYZ789"),)
stmt := aero.NewStatement("test", "demo")stmt.SetFilter(aero.NewContainsWithExpressionFilter( licensesExp, aero.ICT_LIST, "7XYZ789"))stmt.Operations = []*aero.Operation{ aero.SelectByPath("vehicles", aero.EXP_PATH_SELECT_VALUE, aero.CtxAllChildrenWithFilter(matchPlate)), aero.GetBinOp("username"),}
rs, err := client.Query(nil, stmt)as_exp_build(match_plate, as_exp_cmp_eq( as_exp_map_get_by_key(NULL, AS_MAP_RETURN_VALUE, AS_EXP_TYPE_STR, as_exp_str("license"), as_exp_loopvar_map(AS_EXP_LOOPVAR_VALUE)), as_exp_str("7XYZ789")));
as_cdt_ctx proj_ctx;as_cdt_ctx_inita(&proj_ctx, 1);as_cdt_ctx_add_all_children_with_filter(&proj_ctx, match_plate);
as_operations proj_ops;as_operations_inita(&proj_ops, 2);as_operations_select_by_path(&err, &proj_ops, "vehicles", &proj_ctx, AS_EXP_PATH_SELECT_VALUE);as_operations_add_read(&proj_ops, "username");
as_query query;as_query_init(&query, "test", "demo");as_query_where_inita(&query, 1);as_query_where_with_exp(&query, licenses_exp, as_contains(LIST, STRING, "7XYZ789"));query.ops = &proj_ops;
aerospike_query_foreach(&as, &err, NULL, &query, callback, NULL);Exp matchPlate = Exp.EQ( MapExp.GetByKey(MapReturnType.VALUE, Exp.Type.STRING, Exp.Val("license"), Exp.MapLoopVar(LoopVarPart.VALUE)), Exp.Val("7XYZ789"));
Statement stmt = new Statement();stmt.SetNamespace("test");stmt.SetSetName("demo");stmt.SetFilter(Filter.Contains(licensesExp, IndexCollectionType.LIST, "7XYZ789"));stmt.Operations = new Operation[] { CDTOperation.SelectByPath("vehicles", SelectFlag.VALUE, CTX.AllChildrenWithFilter(matchPlate)), Operation.Get("username")};
RecordSet rs = client.Query(null, stmt);const matchPlate = exp.eq( exp.maps.getByKey( exp.loopVarMap(exp.loopVarPart.VALUE), exp.str('license'), exp.type.STR, maps.returnType.VALUE ), exp.str('7XYZ789'))
const projCtx = new Context() .addAllChildrenWithFilter(matchPlate)
const query = client.query('test', 'demo')const sifter = Aerospike.filter.contains(null, '7XYZ789', Aerospike.indexType.LIST)sifter.exp = licensesExpquery.where(sifter)query.ops = [ op.selectByPath('vehicles', exp.pathSelectFlags.VALUE, projCtx), Aerospike.operations.read('username')]
const stream = query.foreach()Each matching record now returns only the projected data: the vehicle with
license "7XYZ789" and the username.
Expected output per record:
{"vehicles": [{"color": "silver", "license": "7XYZ789", "make": "Honda", "model": "Civic"}], "username": "thomasanderson"}Alternatively: query by index name
Once an expression index exists, you can reference it by name instead of
passing the expression to the query filter. This is typically the simpler
production pattern because it avoids rebuilding the expression on the client
side. The same operation projection applies: a selectByPath extracts the
matching vehicle and a bin read returns the username.
Exp matchPlate = Exp.eq( MapExp.getByKey(MapReturnType.VALUE, Exp.Type.STRING, Exp.val("license"), Exp.mapLoopVar(LoopVarPart.VALUE)), Exp.val("7XYZ789"));
Statement stmt = new Statement();stmt.setNamespace("test");stmt.setSetName("demo");stmt.setFilter(Filter.containsByIndex("idx_vehicle_license", IndexCollectionType.LIST, "7XYZ789"));stmt.setOperations(new Operation[] { CdtOperation.selectByPath("vehicles", SelectFlags.VALUE, CTX.allChildrenWithFilter(matchPlate)), Operation.get("username")});
RecordSet rs = client.query(null, stmt);from aerospike import predicates as pfrom aerospike_helpers.operations import operations
match_plate = exp.Eq( exp.MapGetByKey( ctx=None, return_type=aerospike.MAP_RETURN_VALUE, value_type=exp.ResultType.STRING, key=exp.Val("license"), bin=exp.LoopVarMap(aerospike.EXP_LOOPVAR_VALUE) ), exp.Val("7XYZ789")).compile()
query = client.query("test", "demo")query.where_with_index_name("idx_vehicle_license", p.contains(None, aerospike.INDEX_TYPE_LIST, "7XYZ789"))query.add_ops([ operations.select_by_path( bin_name="vehicles", ctx=[cdt_ctx.cdt_ctx_all_children_with_filter(match_plate)], flags=aerospike.EXP_PATH_SELECT_VALUE), operations.read("username")])
results = query.results()matchPlate := aero.ExpEq( aero.ExpMapGetByKey( aero.MapReturnType.VALUE, aero.ExpTypeSTRING, aero.ExpStringVal("license"), aero.ExpMapLoopVar(aero.VALUE), ), aero.ExpStringVal("7XYZ789"),)
stmt := aero.NewStatement("test", "demo")stmt.SetFilter(aero.NewContainsWithIndexNameFilter( "idx_vehicle_license", aero.ICT_LIST, "7XYZ789"))stmt.Operations = []*aero.Operation{ aero.SelectByPath("vehicles", aero.EXP_PATH_SELECT_VALUE, aero.CtxAllChildrenWithFilter(matchPlate)), aero.GetBinOp("username"),}
rs, err := client.Query(nil, stmt)as_exp_build(match_plate, as_exp_cmp_eq( as_exp_map_get_by_key(NULL, AS_MAP_RETURN_VALUE, AS_EXP_TYPE_STR, as_exp_str("license"), as_exp_loopvar_map(AS_EXP_LOOPVAR_VALUE)), as_exp_str("7XYZ789")));
as_cdt_ctx proj_ctx;as_cdt_ctx_inita(&proj_ctx, 1);as_cdt_ctx_add_all_children_with_filter(&proj_ctx, match_plate);
as_operations proj_ops;as_operations_inita(&proj_ops, 2);as_operations_select_by_path(&err, &proj_ops, "vehicles", &proj_ctx, AS_EXP_PATH_SELECT_VALUE);as_operations_add_read(&proj_ops, "username");
as_query query;as_query_init(&query, "test", "demo");as_query_where_inita(&query, 1);as_query_where_with_index_name(&query, "idx_vehicle_license", as_contains(LIST, STRING, "7XYZ789"));query.ops = &proj_ops;
aerospike_query_foreach(&as, &err, NULL, &query, callback, NULL);Exp matchPlate = Exp.EQ( MapExp.GetByKey(MapReturnType.VALUE, Exp.Type.STRING, Exp.Val("license"), Exp.MapLoopVar(LoopVarPart.VALUE)), Exp.Val("7XYZ789"));
Statement stmt = new Statement();stmt.SetNamespace("test");stmt.SetSetName("demo");stmt.SetFilter(Filter.ContainsByIndex("idx_vehicle_license", IndexCollectionType.LIST, "7XYZ789"));stmt.Operations = new Operation[] { CDTOperation.SelectByPath("vehicles", SelectFlag.VALUE, CTX.AllChildrenWithFilter(matchPlate)), Operation.Get("username")};
RecordSet rs = client.Query(null, stmt);const matchPlate = exp.eq( exp.maps.getByKey( exp.loopVarMap(exp.loopVarPart.VALUE), exp.str('license'), exp.type.STR, maps.returnType.VALUE ), exp.str('7XYZ789'))
const projCtx = new Context() .addAllChildrenWithFilter(matchPlate)
const query = client.query('test', 'demo')const sifter = Aerospike.filter.contains(null, '7XYZ789', Aerospike.indexType.LIST)query.whereWithIndexName(sifter, 'idx_vehicle_license')query.ops = [ op.selectByPath('vehicles', exp.pathSelectFlags.VALUE, projCtx), Aerospike.operations.read('username')]
const stream = query.foreach()Expected output per record:
{"vehicles": [{"color": "silver", "license": "7XYZ789", "make": "Honda", "model": "Civic"}], "username": "thomasanderson"}For more on operation projection in queries, see Projection. For more on path expressions, see the path expressions quickstart.