Skip to content

AEL selectors and loop variables

For the complete documentation index see: llms.txt

All documentation pages available in markdown.

Reference page: part of the AEL reference. Covers the full {…} (map) and […] (list) selector punctuation, including plural, range, and inverted forms. See Applies to on the overview page for SDK and Database version requirements.

Key range vs. index range: the marker distinguishes them, not the delimiter. Every dimension uses the same colon (:) for ranges and comma (,) for lists. The leading marker selects the dimension:

  • No marker selects by index ({0:3}).
  • @ selects by key ({@a:d}).
  • = selects by value ({=a:d}).
  • # selects by rank ({#1:5}).

There is no dash (-) selector token in AEL.

/* String key range from "room1" up to (but not including) "room3": */
$.rooms.{@'room1':'room3'}
/* Count of entries in that range: */
$.rooms.{@'room1':'room3'}.count()
/* WRONG: no marker means index dimension, and "room1"/"room3" are not indexes: */
$.rooms.{room1:room3} ← parse error
$.m.{@'a':'d'} /* key range [a, d) */
$.m.{@'a','b','c'} /* key list */
$.m.{0:3} /* index range */
$.m.{=10:20} /* value range */
$.m.{#:3} /* top 3 by rank (lowest 3 ranks) */
$.l.[1:5] /* list index range */
$.l.[=1,2,3] /* list value list */
$.l.[#0:3] /* list rank range */

Map selectors {…}

The first character after { sets the dimension: (none) = index, @ = key, = = value, # = rank.

Selector operands are static literals only (see Collection literals are static only), not parenthesised expressions. Operand types by dimension:

DimensionOperand types
Key (@…)INT, STRING, BLOB literals, NIL, INF
Value (=…)Any scalar literal: INT, FLOAT, STRING, BOOL, BLOB, NIL, INF
Index ({n})INT only — a BLOB or other non-integer literal is a parse error
Rank (#…)INT only — a BLOB or other non-integer literal is a parse error

Examples with BLOB keys: $.perms.{@x'dead'}, $.caps.{@x'aa':x'ff'}, $.caps.{@x'aa',x'bb'}, $.scores.{=x'cafe'}.

In the following selector tables, — means that form isn’t available for that dimension.

DimensionSingularRangeOpen-startOpen-endListInverted rangeInverted list
Index{1}{1:5}{:5}{1:}—{!1:5}—
Key'key' or {@'key'}{@x'ab':x'def0'}{@:'d'}{@'a':}{@'a','b','c'}{!@'a':'d'}{!@'a','b','c'}
Value{=a}{=a:d}{=:d}{=a:}{=a,b,c}{!=a:d}{!=1,2,3}
Rank{#1}{#1:5}{#:5}{#1:}—{!#1:5}—

Relative (map):

FormMeaning
{#-1:1~ref}Rank-relative range
{#-2:~ref}Rank-relative open end
{!#-1:~ref}Inverted rank-relative
{0:1~key}Index range relative to key
{0:~key}Index open end relative to key
{!0:1~key}Inverted index-relative range

Trailing comma: {@k,} is multi-select with one key (invertible as {!@k,}); {@k} alone is singular and non-invertible (zero or one element). The same convention applies to the value dimension: {=a,} / {!=a,} is a one-element value list, while {=a} alone is singular.

Intervals: index and rank ranges use begin-inclusive, end-exclusive semantics.

List selectors […]

After [, if the next non-whitespace character is = the selector is value dimension; if # then rank; if ! then inverted (re-parse the remainder); otherwise index.

Selector operands follow the same rules as the map selectors described earlier: key/value/rank/index literal types as listed there. Examples: $.payload.[='two words'], $.payload.[=x'aa':x'ff'], $.payload.[!=172,].

In the following table, — again means that form isn’t available for that dimension.

DimensionSingularRangeOpen-startOpen-endListInverted rangeInverted list
Index[1][1:5][:5][1:]—[!1:5]—
Value[=a][=a:d][=:d][=a:][=a,b,c][!=a:d][!=a,b,c]
Rank[#1][#1:5][#:5][#1:]—[!#1:5]—

Relative (list):

FormMeaning
[#-3:-1~ref]Rank-relative range
[#-2:~ref]Rank-relative open end
[!#-3:-1~ref]Inverted rank-relative

Trailing comma (value dimension): [=a,] is a multi-select with one value (invertible as [!=a,]); [=a] alone is singular.

Intervals: index and rank ranges use begin-inclusive, end-exclusive semantics.

Inverted selections (prefix !)

$.m.{!@a:d} /* everything except keys a-c */
$.l.[!0:3] /* everything except indices 0-2 */
$.m.{!=temp,draft} /* everything except entries with these values */

Loop variables

Valid only inside *[?(…)] filter predicates and .modify(…) bodies. Requires an enclosing * wildcard in scope (see Wildcard iteration).

FormMeaning
@Current iteration element value (for example, the map value if iterating over map keys)
@.fieldNavigate into current element (map key)
@.[n]List index within current element
@keyParent map key (metadata; no dot)
@indexParent list index (metadata; no dot)

Nested sub-expressions inside filter arguments are not allowed: no filter can nest inside another filter’s path argument. Each *[?(…)] level has its own @ scope.

$.map.{@"key1","key2","key3"}&[?(@ > 100)]
$.store:MAP.*.*[?(@.inStock == true)].title
$.store.book.*.price.modify(@ * 0.9)

Relative selectors (map and list):

$.m.{#-1:1~ref} /* rank-relative range */
$.m.{0:1~key} /* index range relative to key */
$.l.[#-3:-1~ref] /* list rank-relative range */

Selector punctuation quick reference

TokenIn {…} / […]
(none)Index dimension
@Map key dimension (in {…} only)
=Value dimension
#Rank dimension
:Range separator
,List / multi-select
~Relative-to binding
! immediately after { or [Inverted selection

Next steps