Working with nested collection data types
For the complete documentation index see: llms.txt
All documentation pages available in markdown.
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:
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.
- CDT operate API with context selectors for single-element writes and reads at known positions.
- Expression composition (nesting list and map expressions) for reading or filtering nested values within record-level expressions.
- Path expressions for selecting or filtering across multiple nested elements at once.
- String expressions (Database 8.2.0 and later) for transforming String values nested in CDTs, or a context path on the Operate API to write one back in place.
For the Developer SDK, the same nested-data patterns can also be written as
AEL text, for example
"$.vehicles.[0].color == 'white'", instead of composed ListExp/MapExp
calls.
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"List and map ordering
This data model uses two separate ordering choices.
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 so index 0 would no longer be the default. Keep the list unordered so that index 0 stays the default and index 1 is the second choice.
The maps are key-ordered. In this example the maps use the K-ordered subtype, which stores elements in key order. Construct the maps with keys in K-order on the client side for predictable read results. When you read the data back, map entries come back in key order. If the application constructs maps with keys in arbitrary order, the displayed result differs from what was sent, which makes debugging harder.
K-ordered map construction 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: the client has no K-ordered map value. Write the map, then set its order with
MapSetPolicyOpand aKEY_ORDEREDmap policy, as the example does.[]as.MapPairis only how the client returns a K-ordered map; it can’t be written. - C#: pass a
SortedDictionary<string, object>asValue.Get(map, MapOrder.KEY_ORDERED).Value.Get(map)alone writes any dictionary unordered. - 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");
Record record = session.upsert(key) .bin("vehicles").listInsert(0, vehicle, opts -> opts.addUnique().allowFailures()) .bin("vehicles").get() .execute() .getFirstRecord();from aerospike_sdk import SortedMap
vehicle = SortedMap({ "color": "red", "license": "5DEF456", "make": "Ford", "model": "Mustang"})
result = ( session.upsert(key) .bin("vehicles").list_insert(0, vehicle, unique=True, no_fail=True) .bin("vehicles").get() .execute() .first_or_raise())use aerospike::operations::{self, lists::{self, ListOrderType, ListPolicy, ListWriteFlags}};use aerospike::{as_sorted_map, as_val, WritePolicy};
let vehicle = as_sorted_map!( "color" => "red", "license" => "5DEF456", "make" => "Ford", "model" => "Mustang");
let policy = ListPolicy::new_with_flags(ListOrderType::Unordered, [ListWriteFlags::AddUnique, ListWriteFlags::NoFail]);
let record = client.operate(&WritePolicy::default(), &key, &[ lists::insert(&policy, "vehicles", 0, vehicle), operations::get_bin("vehicles"),]).await?;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, MapOrder.KEY_ORDERED)), Operation.Get("vehicles"));// Requires: import as "github.com/aerospike/aerospike-client-go/v8"vehicle := map[string]any{ "color": "red", "license": "5DEF456", "make": "Ford", "model": "Mustang",}
policy := as.NewListPolicy(as.ListOrderUnordered, as.ListWriteFlagsAddUnique|as.ListWriteFlagsNoFail)
record, err := client.Operate(nil, key, as.ListInsertWithPolicyOp(policy, "vehicles", 0, vehicle), // Go writes maps unordered, so make the new vehicle K-ordered as.MapSetPolicyOp(as.NewMapPolicyWithFlags(as.MapOrder.KEY_ORDERED, as.MapWriteFlagsDefault), "vehicles", as.CtxListIndex(0)), as.GetBinOp("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')])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);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, 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), operations.read("vehicles")](key, meta, bins) = client.operate(key, ops)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
list_get_by_index and
map_get_by_key:
list_get_by_indexextracts the map at index 0 from thevehiclesbin.map_get_by_keyextracts the"license"value from that map.- A read operation expression returns the result under the name
"defaultLicense".
Record record = session.query(key) .bin("defaultLicense") .selectFrom("$.vehicles.[0].license:STRING") .execute() .getFirstRecord();
String plate = record.getString("defaultLicense");System.out.println(plate);result = ( session.query(key) .bin("defaultLicense") .select_from("$.vehicles.[0].license:STRING") .first_or_raise())print(result.record.bins["defaultLicense"])use aerospike::expressions::{int_val, list_bin, lists as list_exp, maps as map_exp, string_val, ExpType};use aerospike::operations::exp::{read_exp, ExpReadFlags};use aerospike::{ListReturnType, MapReturnType, WritePolicy};
let read_exp_ = map_exp::get_by_key(MapReturnType::Value, ExpType::STRING, string_val("license".into()), list_exp::get_by_index(ListReturnType::Values, ExpType::MAP, int_val(0), list_bin("vehicles".into()), &[]), &[]);
let record = client.operate(&WritePolicy::default(), &key, &[ read_exp("defaultLicense", read_exp_, ExpReadFlags::Default),]).await?;
println!("{}", record.bins["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");Console.WriteLine(plate);// Requires: import as "github.com/aerospike/aerospike-client-go/v8"readExp := as.ExpMapGetByKey( as.MapReturnType.VALUE, as.ExpTypeSTRING, as.ExpStringVal("license"), as.ExpListGetByIndex( as.ListReturnTypeValue, as.ExpTypeMAP, as.ExpIntVal(0), as.ExpListBin("vehicles"), ),)
record, err := client.Operate(nil, key, as.ExpReadOp("defaultLicense", readExp, as.ExpReadFlagDefault),)
fmt.Println(record.Bins["defaultLicense"])const exp = Aerospike.expconst lists = Aerospike.listsconst maps = Aerospike.maps
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)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");printf("%s\n", plate);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");System.out.println(plate);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"])Expected output: "5DEF456", the license of the Ford Mustang the previous example inserted at index 0.
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 list_get_by_value 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.
DataSet demo = DataSet.of("test", "demo");
RecordStream rs = session.query(demo) .where("'7XYZ789' in $.vehicles:LIST.*.license") .withHint(hint -> hint.allowScansWithWhere()) .execute();from aerospike_sdk import DataSet, QueryHint
demo = DataSet.of("test", "demo")
stream = ( session.query(demo) .where("'7XYZ789' in $.vehicles:LIST.*.license") .with_hint(QueryHint(allow_scans_with_where=True)) .execute())use aerospike::expressions::{exp_select_by_path, list_bin, lists as list_exp, string_val, ExpType};use aerospike::operations::cdt_context::{ctx_all_children, ctx_map_key};use aerospike::operations::path::SelectFlag;use aerospike::{as_val, ListReturnType, QueryPolicy};
let target_plate = "7XYZ789";
let all_licenses = exp_select_by_path(ExpType::LIST, SelectFlag::VALUE, list_bin("vehicles".into()), [ctx_all_children(), ctx_map_key(as_val!("license"))]);
let mut policy = QueryPolicy::default();policy.base_policy.filter_expression = Some(list_exp::get_by_value( ListReturnType::Exists, string_val(target_plate.into()), all_licenses, &[]));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")))));// Requires: import as "github.com/aerospike/aerospike-client-go/v8"targetPlate := "7XYZ789"
allLicenses := as.ExpSelectByPath( as.ExpTypeLIST, as.EXP_PATH_SELECT_VALUE, as.ExpListBin("vehicles"), as.CtxAllChildren(), as.CtxMapKey(as.NewValue("license")),)
queryPolicy := as.NewQueryPolicy()queryPolicy.FilterExpression = as.ExpListGetByValue( as.ListReturnTypeExists, as.ExpStringVal(targetPlate), allLicenses,)const exp = Aerospike.expconst lists = Aerospike.listsconst Context = Aerospike.cdt.Context
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 )}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 = 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"))
policy = { "expressions": exp.ListGetByValue( ctx=None, return_type=aerospike.LIST_RETURN_EXISTS, value=exp.Val(target_plate), bin=all_licenses ).compile()}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.
DataSet demo = DataSet.of("test", "demo");
IndexTask task = session.createIndex(demo, "idx_vehicle_license", IndexType.STRING, IndexCollectionType.LIST, "$.vehicles:LIST.*.license");task.waitTillComplete();from aerospike_sdk import CollectionIndexType, DataSet
demo = DataSet.of("test", "demo")
task = ( session.index(demo) .on_expression("$.vehicles:LIST.*.license") .named("idx_vehicle_license") .string() .collection(CollectionIndexType.LIST) .create())task.wait_till_complete_blocking()use aerospike::expressions::{exp_select_by_path, list_bin, ExpType};use aerospike::operations::cdt_context::{ctx_all_children, ctx_map_key};use aerospike::operations::path::SelectFlag;use aerospike::{as_val, AdminPolicy, CollectionIndexType, IndexType, Task};
let licenses_exp = exp_select_by_path(ExpType::LIST, SelectFlag::VALUE, list_bin("vehicles".into()), [ctx_all_children(), ctx_map_key(as_val!("license"))]);
let task = client.create_index_using_expression(&AdminPolicy::default(), "test", "demo", "idx_vehicle_license", IndexType::String, CollectionIndexType::List, &licenses_exp).await?;task.wait_till_complete(None).await?;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();// Requires: import as "github.com/aerospike/aerospike-client-go/v8"licensesExp := as.ExpSelectByPath( as.ExpTypeLIST, as.EXP_PATH_SELECT_VALUE, as.ExpListBin("vehicles"), as.CtxAllChildren(), as.CtxMapKey(as.NewValue("license")),)
task, err := client.CreateIndexWithExpression(nil, "test", "demo", "idx_vehicle_license", as.STRING, as.ICT_LIST, licensesExp)<-task.OnComplete()const exp = Aerospike.expconst Context = Aerospike.cdt.Context
const ctx = new Context() .addAllChildren() .addMapKey('license')
const licensesExp = exp.selectByPath( exp.binList('vehicles'), exp.type.LIST, exp.pathSelectFlags.VALUE, ctx)
const job = await client.createExpIndex({ ns: 'test', set: 'demo', index: 'idx_vehicle_license', exp: licensesExp, type: Aerospike.indexType.LIST, datatype: Aerospike.indexDataType.STRING})await job.wait()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, 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")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.
from aerospike_sdk import CollectionIndexType, DataSet, Filterfrom aerospike_sdk.exp import Exp
demo = DataSet.of("test", "demo")
stream = ( session.query(demo) .filter( Filter.contains("", "7XYZ789", CollectionIndexType.LIST) .expression(Exp.from_server_compiled_ael("$.vehicles:LIST.*.license")) ) .bin("vehicles").select_from("$.vehicles:LIST.*[?(@.license == '7XYZ789')]") .bin("username").get() .execute())use aerospike::expressions::{eq, exp_map_loop_var, maps as map_exp, string_val, ExpType, LoopVarPart};use aerospike::operations::{self, cdt_context::ctx_all_children_with_filter, path::{self, SelectFlag}};use aerospike::query::Filter;use aerospike::{Bins, CollectionIndexType, MapReturnType, PartitionFilter, QueryPolicy, Statement};
let match_plate = eq( map_exp::get_by_key(MapReturnType::Value, ExpType::STRING, string_val("license".into()), exp_map_loop_var(LoopVarPart::VALUE), &[]), string_val("7XYZ789".into()));
let mut stmt = Statement::new("test", "demo", Bins::All);stmt.add_filter(Filter::contains("", "7XYZ789", CollectionIndexType::List) .expression(licenses_exp));stmt.set_operations(vec![ path::select_by_path("vehicles", SelectFlag::VALUE, [ctx_all_children_with_filter(match_plate)]), operations::get_bin("username"),]);
let rs = client.query(&QueryPolicy::default(), PartitionFilter::all(), stmt).await?;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);// Requires: import as "github.com/aerospike/aerospike-client-go/v8"matchPlate := as.ExpEq( as.ExpMapGetByKey( as.MapReturnType.VALUE, as.ExpTypeSTRING, as.ExpStringVal("license"), as.ExpMapLoopVar(as.VALUE), ), as.ExpStringVal("7XYZ789"),)
stmt := as.NewStatement("test", "demo")stmt.SetFilter(as.NewContainsWithExpressionFilter( licensesExp, as.ICT_LIST, "7XYZ789"))stmt.Operations = []*as.Operation{ as.SelectByPath("vehicles", as.EXP_PATH_SELECT_VALUE, as.CtxAllChildrenWithFilter(matchPlate)), as.GetBinOp("username"),}
rs, err := client.Query(nil, stmt)const exp = Aerospike.expconst maps = Aerospike.mapsconst op = Aerospike.operationsconst Context = Aerospike.cdt.Context
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()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.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()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.
from aerospike_sdk import CollectionIndexType, DataSet, Filter
demo = DataSet.of("test", "demo")
stream = ( session.query(demo) .filter(Filter.contains_by_index("idx_vehicle_license", "7XYZ789", CollectionIndexType.LIST)) .bin("vehicles").select_from("$.vehicles:LIST.*[?(@.license == '7XYZ789')]") .bin("username").get() .execute())use aerospike::expressions::{eq, exp_map_loop_var, maps as map_exp, string_val, ExpType, LoopVarPart};use aerospike::operations::{self, cdt_context::ctx_all_children_with_filter, path::{self, SelectFlag}};use aerospike::query::Filter;use aerospike::{Bins, CollectionIndexType, MapReturnType, PartitionFilter, QueryPolicy, Statement};
let match_plate = eq( map_exp::get_by_key(MapReturnType::Value, ExpType::STRING, string_val("license".into()), exp_map_loop_var(LoopVarPart::VALUE), &[]), string_val("7XYZ789".into()));
let mut stmt = Statement::new("test", "demo", Bins::All);stmt.add_filter(Filter::contains_by_index("idx_vehicle_license", "7XYZ789", CollectionIndexType::List));stmt.set_operations(vec![ path::select_by_path("vehicles", SelectFlag::VALUE, [ctx_all_children_with_filter(match_plate)]), operations::get_bin("username"),]);
let rs = client.query(&QueryPolicy::default(), PartitionFilter::all(), stmt).await?;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);// Requires: import as "github.com/aerospike/aerospike-client-go/v8"matchPlate := as.ExpEq( as.ExpMapGetByKey( as.MapReturnType.VALUE, as.ExpTypeSTRING, as.ExpStringVal("license"), as.ExpMapLoopVar(as.VALUE), ), as.ExpStringVal("7XYZ789"),)
stmt := as.NewStatement("test", "demo")stmt.SetFilter(as.NewContainsWithIndexNameFilter( "idx_vehicle_license", as.ICT_LIST, "7XYZ789"))stmt.Operations = []*as.Operation{ as.SelectByPath("vehicles", as.EXP_PATH_SELECT_VALUE, as.CtxAllChildrenWithFilter(matchPlate)), as.GetBinOp("username"),}
rs, err := client.Query(nil, stmt)const exp = Aerospike.expconst maps = Aerospike.mapsconst op = Aerospike.operationsconst Context = Aerospike.cdt.Context
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()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.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()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.