---
title: "AEL paths and navigation"
description: "AEL reference: record and bin paths, quoted bin names, parenthesised expressions, wildcard iteration, and collection create-order suffixes."
---

# AEL paths and navigation

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

Reference page: part of the [AEL reference](https://aerospike.com/docs/develop/client/sdk/concepts/ael/reference). Covers how AEL navigates from a record to a bin value and into nested collection data. See [Applies to](https://aerospike.com/docs/develop/client/sdk/concepts/ael/reference#applies-to) on the overview page for SDK and Database version requirements.

## Record and bin prefix

Every path starts with `$`:

```plaintext
$                      /* current record */

$.binName              /* scalar bin */

$.profile.name         /* map key access */

$.scores.[0]           /* list index access */

$.data.users.[2].name  /* deeply nested */
```

| Form | Meaning |
| --- | --- |
| `$` | Current record |
| `$.binName` | Bin named `binName` |
| `$."quoted\nname"` | Bin whose name requires quoting or escapes |
| `$.a.b.c` | Navigate map keys `b`, `c` under bin `a` |
| `$.a.[0]` | Navigate list index `0` under bin `a` |

Navigation is strict by default: missing intermediate keys or out-of-range indices cause failure unless a [create-order suffix](#collection-create-order-suffixes) (`:KEY_ORDERED`, `:SORTED`, and so on) applies on that segment.

## Bin type inference

The first context element determines the bin type:

| Path | Inferred type |
| --- | --- |
| `$.x.name` | Map (first context is identifier) |
| `$.x.[0]` | List (first context is `[`) |
| `$.x > 5` | Scalar (no context) |

## Map access

```plaintext
$.profile.name                  /* string key (dot notation) */

$.profile.'special-key'         /* quoted string key (dot notation, for keys with special chars) */

$.m.{@1}                        /* integer key (bare `.1` is not a legal identifier segment) */

$.m.{1}                         /* map by index */

$.m.{='bb'}                     /* map by value */

$.m.{#1}                        /* map by rank */
```

`[…]` bracket notation is reserved for **list** selectors; it is not an alternate syntax for map key access. Use dot notation, quoted dot notation, or the `{@key}` selector for map keys. For the full selector reference (ranges, lists, inverted forms), see [Map selectors `{…}`](https://aerospike.com/docs/develop/client/sdk/concepts/ael/reference/selectors#map-selectors).

### Quoted notation for map key names

These identifiers are grammar keywords or verb-like tokens the parser otherwise expects at that position. Unlike the [reserved words](https://aerospike.com/docs/develop/client/sdk/concepts/ael/reference#lexical-rules) in Lexical rules, most of these are not reserved everywhere in AEL — `type`, `set`, `insert`, and so on are valid method names elsewhere — but using one as a bare dotted map key segment causes a [Debug AEL expressions](https://aerospike.com/docs/develop/client/sdk/concepts/ael/debugging-ael-expressions#parse-and-build-errors). Always use quoted key notation for a map key with one of these names:

-   `and`
-   `append`
-   `clear`
-   `default`
-   `error`
-   `exclusive`
-   `false`
-   `get`
-   `in`
-   `increment`
-   `insert`
-   `let`
-   `not`
-   `or`
-   `remove`
-   `return`
-   `set`
-   `sort`
-   `then`
-   `true`
-   `type`
-   `unknown`
-   `when`

```plaintext
/* Fails as 'type' is a reserved word: */

$.metadata.type == 'fixture'

/* Correct: quoted key notation bypasses keyword parsing */

$.metadata.'type' == 'fixture'
```

## List access

```plaintext
$.scores.[0]                    /* by index */

$.scores.[-1]                   /* last element */

$.scores.[=42]                  /* by value */

$.scores.[#0]                   /* by rank (lowest) */
```

For the full list selector reference (ranges, lists, inverted forms, and relative selectors), see [List selectors `[…]`](https://aerospike.com/docs/develop/client/sdk/concepts/ael/reference/selectors#list-selectors).

## Parenthesised expressions

| Form | Meaning |
| --- | --- |
| `(expr)` | Grouping in ordinary expressions |
| `(expr).segment…` / `(expr).method(…)` | Use a parenthesised expression as the left side of further `.…` navigation or method calls |
| `terminal(expr)` | Full expressions in function-call arguments, for example `setTo($.otherBin)` or `bitSet(offset: 0, size: 8, value: $.data)` |

When `.method()` must follow `(…)`: a dot chain can continue after `()` only when the left side is already a bin path (`$.bin…`), a blob literal, a standalone function call (`max(…)`, `abs(…)`), or an earlier method chain. [Record metadata functions](https://aerospike.com/docs/develop/client/sdk/concepts/ael/reference/functions#record-metadata-functions) and other general expressions must be wrapped: `(expr).method(…)`, for example `($.ttl()).toString()`, not `$.ttl().toString()` (parse error).

Parenthesised expressions aren’t supported inside selector brackets `{…}` or `[…]` in place of literals, for example `$.m.{($.idx)}` is not valid.

Collection literals are static only: list and map literals (`[…]`, `{…}` in value position) must contain literals, not bin paths or other `$` expressions. For example, `[$.hllA, $.hllB]` as an argument to `hllUnionCount` is a parse error (see [HLL path functions](https://aerospike.com/docs/develop/client/sdk/concepts/ael/reference/type-functions#hll-path-functions)). This will change in a later release.

## Wildcard iteration

| Form | Meaning |
| --- | --- |
| `*` | Between dots: iterate all children at this map or list level |
| `.*` | After a path segment: all children of that segment |
| `.*[?(predicate)]` | Children matching a Boolean predicate |

Wildcard `*` as a **path segment** is distinct from `*` as a **literal value** inside `[…]` or `{…}`.

When `*` is the first segment after a bin name, it does not pin the bin’s container type, unlike a list selector or map-key segment. Pin the bin explicitly: `$.bin:LIST.*…` or `$.bin:MAP.*…` (see [Type inference](https://aerospike.com/docs/develop/client/sdk/concepts/ael/reference/type-inference-and-limits#type-inference-summary)).

For loop variables (`@`, `@key`, `@index`) used inside `*[?(…)]` predicates and `.modify(…)` bodies, see [Loop variables](https://aerospike.com/docs/develop/client/sdk/concepts/ael/reference/selectors#loop-variables).

## Key list with filter chain

Restrict a map to specific keys, then filter those entries at the current level:

```plaintext
$.map.{@"key1","key2","key3"}&[?(predicate)]
```

`&[?(` must be contiguous: no spaces between `&`, `[`, and `?`; no extra `.` before `&`.

## Field projection after wildcard

After `.*` or `.*[?(…)]`, select a named field on each matched child with `.fieldName`:

```plaintext
$.store:MAP.*.*[?(@.inStock == true)].title
```

## Collection create-order suffixes

These suffixes attach to collection path segments, including the first segment after `$.` when a top-level container may need to be created. Type pins `:MAP` / `:LIST` (see [Types and type suffixes](https://aerospike.com/docs/develop/client/sdk/concepts/ael/reference#types-and-type-suffixes)) remain typing-only. Each flag tells the server how to **create** a missing container when navigation or a write needs it. Create-order flags attach to the segment where the missing container should be created; each nested level carries its own flag independently, for example `$.a.b:SORTED.[0]:KEY_ORDERED.c.setTo(5)` creates a sorted list at `b`, then a key-ordered map at index `0`.

At most one create-order flag from the following table may follow a single path segment. Combining two is a parse error. `:PERSIST_INDEX` (see [Postfix flags](https://aerospike.com/docs/develop/client/sdk/concepts/ael/reference/control-and-flags#postfix-flags)) may stack with a map create-order flag on the bin root only. Other flags may also stack, for example `:LOCAL`.

| Suffix | Missing value | Existing value |
| --- | --- | --- |
| _(none)_ | Fails — the collection data type (CDT) is not automatically created | Treat as existing map or list |
| `:KEY_ORDERED` | Create empty key-ordered map | Treat as map (no-op for creation) |
| `:KEY_VALUE_ORDERED` | Create empty key-value ordered map | Treat as map (no-op for creation) |
| `:UNORDERED` | Create empty unordered map | Treat as map (no-op for creation) |
| `:SORTED` | Create empty sorted (ordered) list | Treat as list (no-op for creation) |
| `:UNSORTED` | Create empty unsorted (unordered) list — bounded (see [Bounded list writes](#bounded-list-writes-default)); default when a list create is needed | Treat as list (no-op for creation) |
| `:UNSORTED_PAD` | Create empty unsorted list, with nil-padding when navigation or a write needs a distant index (see [Bounded list writes](#bounded-list-writes-default)) | Treat as list (no-op for creation) |

Map segments use `:KEY_ORDERED`, `:KEY_VALUE_ORDERED`, and `:UNORDERED`. List segments use `:SORTED`, `:UNSORTED`, and `:UNSORTED_PAD`. At most one of `:SORTED`, `:UNSORTED`, and `:UNSORTED_PAD` may follow a list segment.

### Bounded list writes (default)

Bounded is the default at two levels, controlled together unless `:UNSORTED_PAD` opts out:

1.  Context create: when a missing list container is created on the navigation path, `:UNSORTED` (the default list create-order) does not nil-pad skipped slots.
2.  List write ops: when writing past the end of an existing, unsorted list (`setTo`, `insert`, `add`, `insertItems`), the server does not nil-pad to reach a sparse index. Writes at `index == list size` are contiguous append, including bulk `insertItems` at the end (all elements append in one call). Bounded forbids sparse growth (index strictly greater than size), not growth in general.

There is no `:BOUNDED` suffix — bounded needs no name. `:UNSORTED_PAD` is the single opt-out: it enables nil-padding on both context create and list-write padding.

::: :unsorted_pad — use with caution
This is not the default (that is bounded `:UNSORTED`, matching the Java and C client default `pad=false` on container create). `:UNSORTED_PAD` opts in to sparse list behavior: if the path next navigates to an index beyond the list end, or a write targets such an index, the server inserts `NIL` for every skipped slot. Writing at index `1000000` on an empty list can materialize on the order of a million nil elements and greatly increase record size. Use only when sparse lists are intentional; prefer `:UNSORTED`, append, or a nearby index when possible.
:::

Sorted lists can never be sparse, so they never require this flag.

```plaintext
/* Nested create: sorted list at 'b', then key-ordered map at index 0 */

$.a.b:SORTED.[0]:KEY_ORDERED.c.setTo('x')

/* Sparse create (see the :UNSORTED_PAD caution earlier in this section) */

$.sparse:UNSORTED_PAD.[1000000].setTo('value')

/* Bare-bin write creates a missing map bin via putItems, not the :MAP type pin */

$.m:MAP.putItems({k: v})
```

### Create-order suffix restrictions by terminal kind

Create-order flags (`:KEY_ORDERED`, `:KEY_VALUE_ORDERED`, `:UNORDERED`, `:SORTED`, `:UNSORTED`, `:UNSORTED_PAD`) may appear only on write paths that can materialize missing containers from collection data type (CDT) context-create bits. They are not allowed on read terminals, and are not allowed on write terminals that do not create containers (`modify()`, `remove()`). A create-order suffix on such paths is a parse error:

1.  **Suffix on a single-select segment.** The suffix must attach to a single-select segment, not a wildcard, filter, or multi-key/rank/index/value list. Invalid: `$.m.*:KEY_ORDERED.k.setTo(1)`, `$.m.{@a,b}:KEY_ORDERED.k.setTo(1)`.
2.  **Every segment before the leaf must be single-select.** An earlier wildcard or multi-select selector invalidates the whole path even when the suffix sits on a later segment. Invalid: `$.m.*.k:KEY_ORDERED.setTo(1)`.
3.  **Terminals that do not create containers reject create-order.** `modify()` and `remove()` never accept create-order flags, even on fully single-select paths. Invalid: `$.m.p1:KEY_ORDERED.k.modify(@ + 1)`, `$.m.p1:KEY_ORDERED.k.remove()`.

When an absent CDT context segment (a path step missing in the bin) should be tolerated for write terminals that do not create containers, use `:NO_FAIL` instead, for example `$.m.p1.{@a,b}.remove():NO_FAIL`. See [Postfix flags](https://aerospike.com/docs/develop/client/sdk/concepts/ael/reference/control-and-flags#postfix-flags).

Qualifying create-order paths honor context-create bits at compile time. Read terminals and non-creating write terminals reject create-order because they never create containers..

## Next steps

-   [Selectors and loop variables](https://aerospike.com/docs/develop/client/sdk/concepts/ael/reference/selectors) — the full `{…}` / `[…]` selector punctuation reference
-   [Functions and terminals](https://aerospike.com/docs/develop/client/sdk/concepts/ael/reference/functions) — path read and write terminals
-   [AEL reference overview](https://aerospike.com/docs/develop/client/sdk/concepts/ael/reference) — lexical rules, literals, and types