---
title: "Context for operations on nested elements"
description: "Learn to target deeply nested List and Map elements in Aerospike using CDT contexts and path expressions."
---

# Context for operations on nested elements

> For the complete documentation index see: [llms.txt](https://aerospike.com/docs/llms.txt)
> 
> All documentation pages available in markdown.

Every [List](https://aerospike.com/docs/develop/data-types/collections/list) and [Map](https://aerospike.com/docs/develop/data-types/collections/map) operation targets a specific element within a list or map bin. For elements at the top level of the collection, no extra addressing is needed. For nested elements, a **context** provides the path from the bin to the target, one selector per nesting level.

-   **Top-level elements:** no context is needed.
-   **Nested elements:** supply a context with one selector per nesting level.
-   **Missing intermediate elements:** use `MAP_KEY_CREATE` or `LIST_INDEX_CREATE` to create the path as you go. See [map context examples](#map-context-examples).

## CDT context API

The following describes the CDT context API in generic terms. Each language client might have slightly different terms to express the same concepts, such as the Java client’s [`CTX` class](https://javadoc.io/doc/com.aerospike/aerospike-client-jdk21/latest/com/aerospike/client/cdt/CTX.html). Most operations in the List and Map API take an optional context parameter.

The context is a list of element selectors targeting a specific nested element. Each element selector includes a context type and a value.

### Element selectors

The following element selectors can be applied starting from the top level of the collection, forming an increasingly deeper path into it.

-   `BY_LIST_INDEX(index)`
-   `BY_LIST_RANK(rank)`
-   `BY_LIST_VALUE(value)`
-   `BY_MAP_INDEX(index)`
-   `BY_MAP_RANK(rank)`
-   `BY_MAP_KEY(key)`
-   `BY_MAP_VALUE(value)`

Each element selector must identify exactly one element. `BY_LIST_VALUE` and `BY_MAP_VALUE` require an exact value, so [WILDCARD](https://aerospike.com/docs/develop/data-types/collections/ordering#wildcard) cannot be used here because it might match multiple elements. WILDCARD is only valid in [list](https://aerospike.com/docs/develop/data-types/collections/list/operations) and [map](https://aerospike.com/docs/develop/data-types/collections/map/operations) `*_by_value` and `*_by_value_list` operations. To select and operate on multiple elements at once, use [path expression contexts](#path-expression-contexts).

::: type matching
Each selector must target an element whose type matches the operation. Applying a list operation to a scalar or a map (or vice versa) causes error 26 (`OP_NOT_APPLICABLE`). See the [error example](#error-selector-targets-wrong-type) below.
:::
::: duplicate values
`BY_LIST_VALUE` and `BY_MAP_VALUE` select the **first** matching element in list or index order when duplicates exist.
:::

### Create-if-missing selectors

The following selectors create an element if it does not exist, then select it. This is similar to how `mkdir -p` creates intermediate directories.

-   `MAP_KEY_CREATE(key)`
-   `LIST_INDEX_CREATE(index)`

See the [map context example](#create-a-nested-map-path) for a worked example.

## List context examples

Consider the following list stored in bin **‘l’**:

```txt
[0, 1, [2, [3, 4], 5, 6], 7, [8, 9]]
```

This list can be visualized as:

```txt
[0, 1, [               ], 7, [    ]]    depth 0 (top level)

        2, [    ], 5, 6       8, 9      depth 1

            3, 4                        depth 2
```

We can operate on the list element `[3, 4]` by identifying a context for the operation using `[BY_LIST_INDEX(2), BY_LIST_INDEX(1)]`.

-   The first selector `BY_LIST_INDEX(2)` selects the third element of the top level list. The element selected by it is the list `[2, [3, 4], 5, 6]`.
-   The second selector `BY_LIST_INDEX(1)` selects the element at index position 1. The element selected by it is the list `[3, 4]`.
-   A list API operation can now be applied to one of the elements within this nested list.

At the top level we have five elements, three of them scalar integer values, two of them are list values.

The list value at index position 2 has four elements: the integer value 2, a list element, then the integer values 5 and 6. Its list element at index position 1 has two elements, the integers 3 and 4.

---

### Append at depth 1

Append the value 100 to the list nested at the last element of the top level.

```txt
# Pseudocode — not runnable

# [0, 1, [2, [3, 4], 5, 6], 7, [8, 9]]

list_append('l', 100, context=[BY_LIST_INDEX(-1)])
```

-   [Java](#tab-panel-2517)
-   [Python](#tab-panel-2518)
-   [C](#tab-panel-2519)
-   [Go](#tab-panel-2520)
-   [C#](#tab-panel-2521)
-   [Node.js](#tab-panel-2522)

```java
Record record = client.operate(null, key,

    ListOperation.append("l", Value.get(100), CTX.listIndex(-1)),

    Operation.get("l")

);
```

```python
ops = [

    list_operations.list_append("l", 100, None,

        [cdt_ctx.cdt_ctx_list_index(-1)])

]

_, _, bins = client.operate(key, ops)
```

```c
as_cdt_ctx ctx;

as_cdt_ctx_inita(&ctx, 1);

as_cdt_ctx_add_list_index(&ctx, -1);

as_operations ops;

as_operations_inita(&ops, 2);

as_integer v;

as_integer_init(&v, 100);

as_operations_list_append(&ops, "l", &ctx, NULL, (as_val*)&v);

as_operations_add_read(&ops, "l");

as_record* rec = NULL;

aerospike_key_operate(&as, &err, NULL, &key, &ops, &rec);
```

```go
record, err := client.Operate(nil, key,

    as.ListAppendWithPolicyContextOp(as.DefaultListPolicy(), "l",

        []*as.CDTContext{as.CtxListIndex(-1)}, 100),

    as.GetBinOp("l"),

)
```

```csharp
Record record = client.Operate(null, key,

    ListOperation.Append("l", Value.Get(100), CTX.ListIndex(-1)),

    Operation.Get("l")

);
```

```js
const ops = [

  lists.append('l', 100).withContext(ctx => ctx.addListIndex(-1)),

  Aerospike.operations.read('l')

]

const result = await client.operate(key, ops)
```

**Result:**

```txt
[0, 1, [2, [3, 4, 100], 5, 6], 7, [8, 9, 100]]
```

Without the context we are appending to the top level list.

```txt
# Pseudocode — not runnable

# [0, 1, [2, [3, 4], 5, 6], 7, [8, 9]]

list_append('l', 100)
```

-   [Java](#tab-panel-2523)
-   [Python](#tab-panel-2524)
-   [C](#tab-panel-2525)
-   [Go](#tab-panel-2526)
-   [C#](#tab-panel-2527)
-   [Node.js](#tab-panel-2528)

```java
Record record = client.operate(null, key,

    ListOperation.append("l", Value.get(100)),

    Operation.get("l")

);
```

```python
ops = [

    list_operations.list_append("l", 100)

]

_, _, bins = client.operate(key, ops)
```

```c
as_operations ops;

as_operations_inita(&ops, 2);

as_integer v;

as_integer_init(&v, 100);

as_operations_list_append(&ops, "l", NULL, NULL, (as_val*)&v);

as_operations_add_read(&ops, "l");

as_record* rec = NULL;

aerospike_key_operate(&as, &err, NULL, &key, &ops, &rec);
```

```go
record, err := client.Operate(nil, key,

    as.ListAppendOp("l", 100),

    as.GetBinOp("l"),

)
```

```csharp
Record record = client.Operate(null, key,

    ListOperation.Append("l", Value.Get(100)),

    Operation.Get("l")

);
```

```js
const ops = [

  lists.append('l', 100),

  Aerospike.operations.read('l')

]

const result = await client.operate(key, ops)
```

**Result:**

```txt
[0, 1, [2, [3, 4], 5, 6], 7, [8, 9], 100]
```

---

### Error: selector targets wrong type

Append the value 100 to a list element at index position 0. There is an integer value at index 0, not a list.

```txt
# Pseudocode — not runnable

# [0, 1, [2, [3, 4], 5, 6], 7, [8, 9]]

list_append('l', 100, context=[BY_LIST_INDEX(0)])

# Error 26 — no change
```

-   [Java](#tab-panel-2529)
-   [Python](#tab-panel-2530)
-   [C](#tab-panel-2531)
-   [Go](#tab-panel-2532)
-   [C#](#tab-panel-2533)
-   [Node.js](#tab-panel-2534)

```java
Record record = client.operate(null, key,

    ListOperation.append("l", Value.get(100), CTX.listIndex(0)),

    Operation.get("l")

);

// throws AerospikeException: Error 26 OP_NOT_APPLICABLE
```

```python
ops = [

    list_operations.list_append("l", 100, None,

        [cdt_ctx.cdt_ctx_list_index(0)])

]

# raises exception: error 26 OP_NOT_APPLICABLE

_, _, bins = client.operate(key, ops)
```

```c
as_cdt_ctx ctx;

as_cdt_ctx_inita(&ctx, 1);

as_cdt_ctx_add_list_index(&ctx, 0);

as_operations ops;

as_operations_inita(&ops, 2);

as_integer v;

as_integer_init(&v, 100);

as_operations_list_append(&ops, "l", &ctx, NULL, (as_val*)&v);

as_operations_add_read(&ops, "l");

as_record* rec = NULL;

// returns AEROSPIKE_ERR_OP_NOT_APPLICABLE (error 26)

aerospike_key_operate(&as, &err, NULL, &key, &ops, &rec);
```

```go
record, err := client.Operate(nil, key,

    as.ListAppendWithPolicyContextOp(as.DefaultListPolicy(), "l",

        []*as.CDTContext{as.CtxListIndex(0)}, 100),

    as.GetBinOp("l"),

)

// err: OP_NOT_APPLICABLE (error 26)
```

```csharp
Record record = client.Operate(null, key,

    ListOperation.Append("l", Value.Get(100), CTX.ListIndex(0)),

    Operation.Get("l")

);

// throws AerospikeException: Error 26 OP_NOT_APPLICABLE
```

```js
const ops = [

  lists.append('l', 100).withContext(ctx => ctx.addListIndex(0)),

  Aerospike.operations.read('l')

]

// throws AerospikeError: error 26 OP_NOT_APPLICABLE

const result = await client.operate(key, ops)
```

**Error:** error code 26 `OP_NOT_APPLICABLE`

A list context selector must target a list element, and a map context selector must target a map element. Applying a list operation to a scalar value fails.

---

### Append at depth 2

Append the value 100 to the deepest list, which is at depth 2. This requires a context with two selectors to navigate to that list.

```txt
# Pseudocode — not runnable

# [0, 1, [2, [3, 4], 5, 6], 7, [8, 9]]

list_append('l', 100, context=[BY_LIST_INDEX(2), BY_LIST_INDEX(1)])
```

-   [Java](#tab-panel-2535)
-   [Python](#tab-panel-2536)
-   [C](#tab-panel-2537)
-   [Go](#tab-panel-2538)
-   [C#](#tab-panel-2539)
-   [Node.js](#tab-panel-2540)

```java
Record record = client.operate(null, key,

    ListOperation.append("l", Value.get(100),

        CTX.listIndex(2), CTX.listIndex(1)),

    Operation.get("l")

);
```

```python
ops = [

    list_operations.list_append("l", 100, None,

        [cdt_ctx.cdt_ctx_list_index(2), cdt_ctx.cdt_ctx_list_index(1)])

]

_, _, bins = client.operate(key, ops)
```

```c
as_cdt_ctx ctx;

as_cdt_ctx_inita(&ctx, 2);

as_cdt_ctx_add_list_index(&ctx, 2);

as_cdt_ctx_add_list_index(&ctx, 1);

as_operations ops;

as_operations_inita(&ops, 2);

as_integer v;

as_integer_init(&v, 100);

as_operations_list_append(&ops, "l", &ctx, NULL, (as_val*)&v);

as_operations_add_read(&ops, "l");

as_record* rec = NULL;

aerospike_key_operate(&as, &err, NULL, &key, &ops, &rec);
```

```go
record, err := client.Operate(nil, key,

    as.ListAppendWithPolicyContextOp(as.DefaultListPolicy(), "l",

        []*as.CDTContext{as.CtxListIndex(2), as.CtxListIndex(1)}, 100),

    as.GetBinOp("l"),

)
```

```csharp
Record record = client.Operate(null, key,

    ListOperation.Append("l", Value.Get(100),

        CTX.ListIndex(2), CTX.ListIndex(1)),

    Operation.Get("l")

);
```

```js
const ops = [

  lists.append('l', 100)

    .withContext(ctx => ctx.addListIndex(2).addListIndex(1)),

  Aerospike.operations.read('l')

]

const result = await client.operate(key, ops)
```

**Result:**

```txt
[0, 1, [2, [3, 4, 100], 5, 6], 7, [8, 9]]
```

---

### Select by value with duplicates

In this example bin **‘l’** contains a list of tuples, including duplicates:

```txt
[[1, 1], [2, 2], [2, 2, 2], [2, 2], [1, 1]]
```

This list can be visualized as:

```txt
[ [    ], [    ], [       ], [    ], [    ] ]    depth 0 (top level)

   1, 1    2, 2    2, 2, 2    2, 2    1, 1      depth 1
```

We select the sub-list `[2, 2]` using `BY_LIST_VALUE` with an exact value. Because there are multiple matching elements, the selector picks the **first** one encountered in list order.

```txt
# Pseudocode — not runnable

# [[1, 1], [2, 2], [2, 2, 2], [2, 2], [1, 1]]

list_append('l', 3, context=[BY_LIST_VALUE([2, 2])])
```

-   [Java](#tab-panel-2541)
-   [Python](#tab-panel-2542)
-   [C](#tab-panel-2543)
-   [Go](#tab-panel-2544)
-   [C#](#tab-panel-2545)
-   [Node.js](#tab-panel-2546)

```java
List<Value> matchValue = List.of(Value.get(2), Value.get(2));

Record record = client.operate(null, key,

    ListOperation.append("l", Value.get(3),

        CTX.listValue(Value.get(matchValue))),

    Operation.get("l")

);
```

```python
ops = [

    list_operations.list_append("l", 3, None,

        [cdt_ctx.cdt_ctx_list_value([2, 2])])

]

_, _, bins = client.operate(key, ops)
```

```c
as_arraylist match_val;

as_arraylist_inita(&match_val, 2);

as_arraylist_append_int64(&match_val, 2);

as_arraylist_append_int64(&match_val, 2);

as_cdt_ctx ctx;

as_cdt_ctx_inita(&ctx, 1);

as_cdt_ctx_add_list_value(&ctx, (as_val*)&match_val);

as_operations ops;

as_operations_inita(&ops, 2);

as_integer v;

as_integer_init(&v, 3);

as_operations_list_append(&ops, "l", &ctx, NULL, (as_val*)&v);

as_operations_add_read(&ops, "l");

as_record* rec = NULL;

aerospike_key_operate(&as, &err, NULL, &key, &ops, &rec);
```

```go
record, err := client.Operate(nil, key,

    as.ListAppendWithPolicyContextOp(as.DefaultListPolicy(), "l",

        []*as.CDTContext{

            as.CtxListValue(as.NewValue([]int{2, 2})),

        }, 3),

    as.GetBinOp("l"),

)
```

```csharp
IList<Value> matchValue = new List<Value> { Value.Get(2), Value.Get(2) };

Record record = client.Operate(null, key,

    ListOperation.Append("l", Value.Get(3),

        CTX.ListValue(Value.Get(matchValue))),

    Operation.Get("l")

);
```

```js
const ops = [

  lists.append('l', 3)

    .withContext(ctx => ctx.addListValue([2, 2])),

  Aerospike.operations.read('l')

]

const result = await client.operate(key, ops)
```

**Result:**

The sub-list `[2, 2]` at index 1 is selected and gets the appended element. The duplicate `[2, 2]` at index 3 is not modified.

```txt
[[1, 1], [2, 2, 3], [2, 2, 2], [2, 2], [1, 1]]
```

## Map context examples

### Create a nested map path

The `MAP_KEY_CREATE` selector creates intermediate map entries that do not yet exist, then selects them. This is useful when building up nested structures incrementally.

In the following example we want to add accolades to the stats of an actor. The data is in bin **‘m’**:

```json
{"name": "chuck norris"}
```

We want to [`increment`](https://aerospike.com/docs/develop/data-types/collections/map/operations#increment) a jokes accolade by 317, but neither `"stats"`, `"accolades"`, nor `"jokes"` exists yet. Using `MAP_KEY_CREATE`, the operation creates the full path and succeeds.

```txt
# Pseudocode — not runnable

map_increment('m', 'jokes', 317, context=[MAP_KEY_CREATE('stats'), MAP_KEY_CREATE('accolades')])
```

-   [Java](#tab-panel-2547)
-   [Python](#tab-panel-2548)
-   [C](#tab-panel-2549)
-   [Go](#tab-panel-2550)
-   [C#](#tab-panel-2551)
-   [Node.js](#tab-panel-2552)

```java
Record record = client.operate(null, key,

    MapOperation.increment(MapPolicy.Default, "m",

        Value.get("jokes"), Value.get(317),

        CTX.mapKeyCreate(Value.get("stats"), MapOrder.UNORDERED),

        CTX.mapKeyCreate(Value.get("accolades"), MapOrder.UNORDERED)),

    Operation.get("m")

);
```

```python
ops = [

    map_operations.map_increment("m", "jokes", 317, None,

        [cdt_ctx.cdt_ctx_map_key_create("stats", aerospike.MAP_UNORDERED),

         cdt_ctx.cdt_ctx_map_key_create("accolades", aerospike.MAP_UNORDERED)])

]

_, _, bins = client.operate(key, ops)
```

```c
as_cdt_ctx ctx;

as_cdt_ctx_inita(&ctx, 2);

as_cdt_ctx_add_map_key_create(&ctx,

    (as_val*)as_string_new("stats", false), AS_MAP_UNORDERED);

as_cdt_ctx_add_map_key_create(&ctx,

    (as_val*)as_string_new("accolades", false), AS_MAP_UNORDERED);

as_operations ops;

as_operations_inita(&ops, 2);

as_string mk;

as_string_init(&mk, "jokes", false);

as_integer mv;

as_integer_init(&mv, 317);

as_operations_map_increment(&ops, "m", &ctx, NULL,

    (as_val*)&mk, (as_val*)&mv);

as_operations_add_read(&ops, "m");

as_record* rec = NULL;

aerospike_key_operate(&as, &err, NULL, &key, &ops, &rec);
```

```go
record, err := client.Operate(nil, key,

    as.MapIncrementOp(as.DefaultMapPolicy(), "m",

        "jokes", 317,

        as.CtxMapKeyCreate(as.StringValue("stats"), as.MapOrder.UNORDERED),

        as.CtxMapKeyCreate(as.StringValue("accolades"), as.MapOrder.UNORDERED)),

    as.GetBinOp("m"),

)
```

```csharp
Record record = client.Operate(null, key,

    MapOperation.Increment(MapPolicy.Default, "m",

        Value.Get("jokes"), Value.Get(317),

        CTX.MapKeyCreate(Value.Get("stats"), MapOrder.UNORDERED),

        CTX.MapKeyCreate(Value.Get("accolades"), MapOrder.UNORDERED)),

    Operation.Get("m")

);
```

```js
const context = new Aerospike.cdt.Context()

  .addMapKeyCreate('stats')

  .addMapKeyCreate('accolades')

const ops = [

  maps.increment('m', 'jokes', 317)

    .withContext(context),

  Aerospike.operations.read('m')

]

const result = await client.operate(key, ops)
```

**Result:**

```txt
{"name": "chuck norris", "stats": {"accolades": {"jokes": 317}}}
```

## Path expression contexts

The contexts described above select a single element at each level. Starting with Aerospike Database 8.1.1, [path expressions](https://aerospike.com/docs/develop/expressions/path/) extend this model to select and filter **multiple elements at once**, using `selectByPath` and `modifyByPath` instead of traditional single-element CDT operations.

### Matching and filtering (8.1.1+)

-   `ALL_CHILDREN` - matches all children of the current Map or List without filtering.
-   `ALL_CHILDREN_WITH_FILTER(exp)` - matches children of the current Map or List where the filter expression evaluates to `true`. The filter can assign an aspect of the current element (its value, key or index) to a [loop variable](https://aerospike.com/docs/develop/expressions/path/advanced#accessing-current-element-data-from-a-map-key-list-index-or-element-value-with-a-loopvar).

The following example uses the [vehicles](https://aerospike.com/docs/develop/expressions/nesting) data model, a list of maps within a bin called `vehicles`. It reads the license plates of all vehicles where `make` is `"Tesla"`:

-   [Java](#tab-panel-2553)
-   [Python](#tab-panel-2554)
-   [Go](#tab-panel-2555)
-   [C](#tab-panel-2556)
-   [C#](#tab-panel-2557)
-   [Node.js](#tab-panel-2558)

```java
Exp isTesla = Exp.eq(

    MapExp.getByKey(MapReturnType.VALUE, Exp.Type.STRING,

        Exp.val("make"), Exp.mapLoopVar(LoopVarPart.VALUE)),

    Exp.val("Tesla"));

Record record = client.operate(null, key,

    CdtOperation.selectByPath("vehicles", SelectFlags.VALUE,

        CTX.allChildrenWithFilter(isTesla),

        CTX.mapKey(Value.get("license"))));
```

```python
is_tesla = exp.Eq(

    exp.MapGetByKey(

        ctx=None,

        return_type=aerospike.MAP_RETURN_VALUE,

        value_type=exp.ResultType.STRING,

        key=exp.Val("make"),

        bin=exp.LoopVarMap(aerospike.EXP_LOOPVAR_VALUE)

    ),

    exp.Val("Tesla")

).compile()

ctx = [

    cdt_ctx.cdt_ctx_all_children_with_filter(is_tesla),

    cdt_ctx.cdt_ctx_map_key("license"),

]

ops = [

    operations.select_by_path(

        bin_name="vehicles", ctx=ctx,

        flags=aerospike.EXP_PATH_SELECT_VALUE)

]

_, _, bins = client.operate(key, ops)
```

```go
isTesla := as.ExpEq(

    as.ExpMapGetByKey(

        as.MapReturnType.VALUE, as.ExpTypeSTR,

        as.ExpStringVal("make"),

        as.ExpMapLoopVar(as.VALUE),

    ),

    as.ExpStringVal("Tesla"),

)

record, err := client.Operate(nil, key,

    as.SelectByPath("vehicles", as.EXP_PATH_SELECT_VALUE,

        as.CtxAllChildrenWithFilter(isTesla),

        as.CtxMapKey(as.NewStringValue("license"))),

)
```

```c
as_exp_build(is_tesla,

    as_exp_cmp_eq(

        as_exp_map_get_by_key(NULL, AS_MAP_RETURN_VALUE, AS_EXP_TYPE_STR,

            as_exp_str("make"),

            as_exp_loopvar_map(AS_EXP_LOOPVAR_VALUE)),

        as_exp_str("Tesla"))

);

as_cdt_ctx ctx;

as_cdt_ctx_inita(&ctx, 2);

as_cdt_ctx_add_all_children_with_filter(&ctx, is_tesla);

as_cdt_ctx_add_map_key(&ctx, (as_val*)as_string_new("license", false));

as_operations ops;

as_operations_inita(&ops, 1);

as_operations_select_by_path(&err, &ops, "vehicles", &ctx,

    AS_EXP_PATH_SELECT_VALUE);

as_record* rec = NULL;

aerospike_key_operate(&as, &err, NULL, &key, &ops, &rec);
```

```csharp
Exp isTesla = Exp.EQ(

    MapExp.GetByKey(MapReturnType.VALUE, Exp.Type.STRING,

        Exp.Val("make"), Exp.MapLoopVar(LoopVarPart.VALUE)),

    Exp.Val("Tesla"));

Record record = client.Operate(null, key,

    CDTOperation.SelectByPath("vehicles", SelectFlag.VALUE,

        CTX.AllChildrenWithFilter(isTesla),

        CTX.MapKey(Value.Get("license")))

);
```

```js
const isTesla = exp.eq(

  exp.maps.getByKey(

    exp.loopVarMap(exp.loopVarPart.VALUE),

    exp.str('make'),

    exp.type.STR,

    maps.returnType.VALUE

  ),

  exp.str('Tesla')

)

const context = new Context()

  .addAllChildrenWithFilter(isTesla)

  .addMapKey('license')

const ops = [

  op.selectByPath('vehicles', exp.pathSelectFlags.VALUE, context)

]

const result = await client.operate(key, ops)
```

Expected output: `["6ABC123"]`

### Key selection and combined filtering (8.1.2+)

-   `MAP_KEYS_IN(keys...)` - select map entries whose keys match any of the provided values. This is equivalent to a SQL `WHERE key IN (k1, k2, ...)` clause and uses the map’s internal index for efficient lookup.
-   `AND_FILTER(exp)` - apply an additional filter expression at the same level as the preceding context. Entries must satisfy both the preceding context and this filter to be included. You can only have **one expression at any level**, so `AND_FILTER`:
    -   Cannot be chained after a previous `AND_FILTER(exp)`.
    -   Cannot be used after an `ALL_CHILDREN` or `ALL_CHILDREN_WITH_FILTER` context.
    -   Not supported inside expression-wrapped operations (`CdtExp.selectByPath`). Use direct CDT operations (`CdtOperation.selectByPath` / `CdtOperation.modifyByPath`) instead.

-   [Java](#tab-panel-2559)
-   [Python](#tab-panel-2560)
-   [Go](#tab-panel-2561)
-   [C](#tab-panel-2562)
-   [C#](#tab-panel-2563)

```java
CdtOperation.selectByPath("myBin", SelectFlags.MATCHING_TREE | SelectFlags.NO_FAIL,

    CTX.mapKeysIn(10001L, 10003L),          // select rooms by ID

    CTX.andFilter(Exp.and(timeFilter, deletedFilter)),  // additional room-level filters

    CTX.allChildren(),                       // descend into each room's children

    CTX.allChildrenWithFilter(rateFilter));  // filter at the rates level
```

```python
ctx = [

    cdt_ctx.cdt_ctx_map_keys_in([10001, 10003]),          # select rooms by ID

    cdt_ctx.cdt_ctx_and_filter(

        exp.And(time_filter, deleted_filter).compile()),   # additional room-level filters

    cdt_ctx.cdt_ctx_all_children(),                        # descend into each room's children

    cdt_ctx.cdt_ctx_all_children_with_filter(rate_filter), # filter at the rates level

]

ops = [

    operations.select_by_path(

        bin_name="myBin", ctx=ctx,

        flags=aerospike.EXP_PATH_SELECT_MATCHING_TREE | aerospike.EXP_PATH_SELECT_NO_FAIL),

]

_, _, bins = client.operate(key, ops)
```

```go
record, err := client.Operate(nil, key,

    as.SelectByPath("myBin",

        as.EXP_PATH_SELECT_MATCHING_TREE|as.EXP_PATH_SELECT_NO_FAIL,

        as.CtxMapIntKeysIn(10001, 10003),           // select rooms by ID

        as.CtxAndFilter(as.ExpAnd(timeFilter, deletedFilter)), // additional room-level filters

        as.CtxAllChildren(),                         // descend into each room's children

        as.CtxAllChildrenWithFilter(rateFilter)),    // filter at the rates level

)
```

```c
as_arraylist room_ids;

as_arraylist_inita(&room_ids, 2);

as_arraylist_append_int64(&room_ids, 10001);

as_arraylist_append_int64(&room_ids, 10003);

as_exp_build(room_level_filter, as_exp_and(time_filter, deleted_filter));

as_cdt_ctx ctx;

as_cdt_ctx_inita(&ctx, 4);

as_cdt_ctx_add_map_keys_in(&ctx, (as_list*)&room_ids);    // select rooms by ID

as_cdt_ctx_add_and_filter(&ctx, room_level_filter);       // additional room-level filters

as_cdt_ctx_add_all_children(&ctx);                        // descend into each room's children

as_cdt_ctx_add_all_children_with_filter(&ctx, rate_filter); // filter at the rates level

as_operations ops;

as_operations_inita(&ops, 1);

as_operations_select_by_path(&err, &ops, "myBin", &ctx,

    AS_EXP_PATH_SELECT_MATCHING_TREE | AS_EXP_PATH_SELECT_NO_FAIL);

as_record* rec = NULL;

aerospike_key_operate(&as, &err, NULL, &key, &ops, &rec);
```

```csharp
Record record = client.Operate(null, key,

    CDTOperation.SelectByPath("myBin",

        SelectFlag.MATCHING_TREE | SelectFlag.NO_FAIL,

        CTX.MapKeysIn(10001L, 10003L),                      // select rooms by ID

        CTX.AndFilter(Exp.And(timeFilter, deletedFilter)),  // additional room-level filters

        CTX.AllChildren(),                                   // descend into each room's children

        CTX.AllChildrenWithFilter(rateFilter))               // filter at the rates level

);
```

For usage examples and performance comparisons, see [path expressions performance](https://aerospike.com/docs/develop/expressions/path/performance).

### Language-specific client APIs

-   [Java](https://javadoc.io/doc/com.aerospike/aerospike-client-jdk21/latest/com/aerospike/client/cdt/CTX.html)
-   [C#](https://aerospike.com/apidocs/csharp/api/Aerospike.Client.CTX.html)
-   [Go](https://godoc.org/github.com/aerospike/aerospike-client-go#CDTContext)
-   [C](https://aerospike.com/apidocs/c/dc/dd1/structas__cdt__ctx.html)
-   [Rust](https://docs.rs/aerospike/latest/aerospike/operations/struct.CdtContext.html)
-   [Python](https://aerospike-python-client.readthedocs.io/en/latest/aerospike_helpers.cdt_ctx.html)
-   [Node.js](https://aerospike.com/apidocs/nodejs/classes/cdt.context)
-   [PHP](https://aerospike.github.io/php-client/classes/Aerospike-Context.html)