Skip to content

AEL paths and navigation

For the complete documentation index see: llms.txt

All documentation pages available in markdown.

Reference page: part of the AEL reference. Covers how AEL navigates from a record to a bin value and into nested collection data. See Applies to on the overview page for SDK and Database version requirements.

Record and bin prefix

Every path starts with $:

$ /* current record */
$.binName /* scalar bin */
$.profile.name /* map key access */
$.scores.[0] /* list index access */
$.data.users.[2].name /* deeply nested */
FormMeaning
$Current record
$.binNameBin named binName
$."quoted\nname"Bin whose name requires quoting or escapes
$.a.b.cNavigate 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 (:KEY_ORDERED, :SORTED, and so on) applies on that segment.

Bin type inference

The first context element determines the bin type:

PathInferred type
$.x.nameMap (first context is identifier)
$.x.[0]List (first context is [)
$.x > 5Scalar (no context)

Map access

$.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 {…}.

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 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. 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
/* Fails as 'type' is a reserved word: */
$.metadata.type == 'fixture'
/* Correct: quoted key notation bypasses keyword parsing */
$.metadata.'type' == 'fixture'

List access

$.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 […].

Parenthesised expressions

FormMeaning
(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 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). This will change in a later release.

Wildcard iteration

FormMeaning
*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).

For loop variables (@, @key, @index) used inside *[?(…)] predicates and .modify(…) bodies, see Loop variables.

Key list with filter chain

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

$.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:

$.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) 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) may stack with a map create-order flag on the bin root only. Other flags may also stack, for example :LOCAL.

SuffixMissing valueExisting value
(none)Fails — the collection data type (CDT) is not automatically createdTreat as existing map or list
:KEY_ORDEREDCreate empty key-ordered mapTreat as map (no-op for creation)
:KEY_VALUE_ORDEREDCreate empty key-value ordered mapTreat as map (no-op for creation)
:UNORDEREDCreate empty unordered mapTreat as map (no-op for creation)
:SORTEDCreate empty sorted (ordered) listTreat as list (no-op for creation)
:UNSORTEDCreate empty unsorted (unordered) list — bounded (see Bounded list writes); default when a list create is neededTreat as list (no-op for creation)
:UNSORTED_PADCreate empty unsorted list, with nil-padding when navigation or a write needs a distant index (see Bounded list writes)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.

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

/* 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.

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