---
title: "AEL functions and terminals"
description: "AEL reference: record metadata functions, standalone numeric/geo functions, and path read/write terminals for map and list data."
---

# AEL functions and terminals

> 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 functions with no collection data type (CDT)-typed receiver, and the terminals that end a path. 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 metadata functions

All forms are invoked on `$` with `()`.

```plaintext
$.ttl() < 3600                        /* expires in < 1 hour */

$.recordSize() > 1024                 /* large records */

$.timeSinceLastUpdate() < 7200000     /* updated in last 2 hours */

$.isTombstone()                       /* deleted records */

$.setName() == 'critical'

$.digestModulo(3) == 0                /* partition sampling */
```

| Function | Returns | Description |
| --- | --- | --- |
| `$.ttl()` | `INT` | Remaining time-to-live in seconds |
| `$.voidTime()` | `INT` | Absolute expiry time in nanoseconds (second resolution); `-1` = never expires |
| `$.lastUpdateTime()` | `INT` | Last update time in nanoseconds since Unix epoch (millisecond resolution) |
| `$.timeSinceLastUpdate()` | `INT` | Time since last update in milliseconds |
| `$.setName()` | `STRING` | Record’s set name |
| `$.key()` | `INT`, `STRING`, or `BLOB` | Record primary key; optional `:TYPE` after `()` |
| `$.keyExists()` | `BOOL` | Whether user key is stored |
| `$.isTombstone()` | `BOOL` | Whether record is deleted |
| `$.recordSize()` | `INT` | Total record size in bytes (introduced Database v7.0.0) |
| `$.deviceSize()` | `INT` | Device size in bytes. Deprecated as of Database v8.1.0; use `recordSize()` instead |
| `$.memorySize()` | `INT` | Memory size in bytes. Deprecated as of Database v8.1.0; use `recordSize()` instead |
| `$.digestModulo(n)` | `INT` | Digest modulo `n` (unitless; `n` is a positive integer) |

Unlike path-level `exists()` (see [Path read terminals](#path-read-terminals)), `keyExists()` and `isTombstone()` describe record metadata that is always resolvable. They return plain `BOOL` rather than `TRILEAN` and never evaluate to `unknown`.

In a read, `isTombstone()` is always `false`, because a read of a tombstone finds no record. It’s meaningful in [Cross Datacenter Replication (XDR) filter expressions](https://aerospike.com/docs/database/manage/xdr/filters) and in expressions a write request evaluates, where it’s `true` if the record was a tombstone.

Chaining a method off a metadata call requires parentheses around the metadata call: `($.ttl()).toString()` works, but `$.ttl().toString()` is a parse error. See [Parenthesised expressions](https://aerospike.com/docs/develop/client/sdk/concepts/ael/reference/paths#parenthesised-expressions).

`voidTime()` and `lastUpdateTime()` return nanosecond-scale integers with coarser actual precision (second and millisecond granularity, respectively). `timeSinceLastUpdate()` returns plain milliseconds.

`type()` applies only to a bare bin path, meaning `$.bin` itself rather than a nested path into its contents: `$.bin.type()`. There is no record-level `$.type()` on `$` alone. `exists()` is not limited to bare bins — it’s also a general [path read terminal](#path-read-terminals) usable after any navigation, for example `$.profile.email.exists()` to test a nested map key.

## Standalone functions

Functions with no path receiver (single-argument calls use one positional argument unless noted):

| Function | Parameters | Return | Description |
| --- | --- | --- | --- |
| `abs(x)` | numeric | same as `x` | Absolute value |
| `ceil(x)` / `floor(x)` | `FLOAT` | `FLOAT` | Ceiling / floor |
| `min(a, b, …)` / `max(a, b, …)` | numeric varargs | same type | Minimum / maximum |
| `countOneBits(x)` | `INT` | `INT` | Population count |
| `findBitLeft(x:, value:)` / `findBitRight(x:, value:)` | `INT`, `TRILEAN` | `INT` | Scan for bit value scanning left/right. `value:` — `true` = find a set bit, `false` = find a clear bit |
| `log(value:, base:)` / `pow(base:, exponent:)` | `FLOAT` | `FLOAT` | Logarithm / power (named parameters) |
| `exclusive(a, b, …)` | `TRILEAN` (varargs) | `TRILEAN` | Exactly one operand `true`; see [Logical operators](https://aerospike.com/docs/develop/client/sdk/concepts/ael/reference/operators#logical-operators) |

```plaintext
ceil($.price)                              /* round up */

floor($.price)                             /* round down */

min($.a:INT, $.b, $.c)                     /* smallest of three bins */

countOneBits($.flags)                      /* number of set bits */

findBitLeft(x: $.flags, value: true)       /* index of the leftmost set bit */

findBitRight(x: $.flags, value: false)     /* index of the rightmost clear bit */

log(value: 128.0, base: 2.0)               /* 7.0 */

pow(base: 2.0, exponent: 10.0)             /* 1024.0 */
```

### GeoJSON

| Function | Parameters | Return | Description |
| --- | --- | --- | --- |
| `geoJson('…')` | `STRING` (JSON) | `GEO` | Constructs a `GEO` value from a GeoJSON string literal |
| `geoCompare(a, b)` | `GEO`, `GEO` | `TRILEAN` | Bidirectional spatial containment: `true` if either value contains, or is contained within, the other. Positional |

`geoJson`’s argument must be a string literal at parse time, for example `geoJson('{"type":"Point","coordinates":[-122.4,37.7]}')`. Dynamic forms such as `geoJson($.str)` are not supported.

```plaintext
geoCompare($.loc, geoJson('{"type":"Point","coordinates":[-122.349,47.620]}'))

geoCompare(geoJson('{"type":"AeroCircle","coordinates":[[-122.0,37.4],3000.0]}'), $.loc)
```

Bins typed as `GEO` are recognized automatically inside `geoCompare(...)`. Use with a [GEO2DSPHERE secondary index](https://aerospike.com/docs/develop/data-types/geospatial) on the bin. In Python, write GeoJSON bins with `set_to_geo_json(...)`. The Java Developer SDK’s `setToGeoJson(...)` is equivalent. For example, `.bin("geo").setToGeoJson("{\"type\":\"Point\",\"coordinates\":[-122.4194, 37.7749]}")`. See [Update records](https://aerospike.com/docs/develop/client/sdk/usage/update) for more information about updating records.

## Path read terminals

There is no generic `get()` function in AEL. End a path after navigation/selectors/iteration with a named terminal to pick an explicit result shape, for example `$.mapBin.{@a,b}.getKeyValues()` or `$.listBin.[0:3].count()`. To pin a type instead, use the `:TYPE` suffix on the path (see [Types and type suffixes](https://aerospike.com/docs/develop/client/sdk/concepts/ael/reference#types-and-type-suffixes)), for example `$.binName:INT`. If no terminal is written, implicit get returns matched values (multi-select → flat `LIST`).

| Terminal | Return | Description |
| --- | --- | --- |
| _(implicit get)_ | scalar or `LIST` | Default: values at path |
| `getKeys()` | `LIST` | Keys of matched elements (multi-select required) |
| `getKeyValues()` | `LIST` | Flat `[k,v,k,v,…]` — not a `MAP` |
| `getMaps()` | `MAP` | Key → value map (key-ordered by default) |
| `getTree()` | `MAP` | Structure-preserving tree |
| `getIndexes()` | `INT` or `LIST` | Index(es) of matched elements |
| `getRanks()` | `INT` or `LIST` | Rank(s) of matched elements |
| `count()` | `INT` | Element count on a single-select `LIST`/`MAP` path; match count on multi-select |
| `exists()` | `TRILEAN` | Whether path/value exists |
| `toInt()` / `toFloat()` | numeric | Cast path result (`STRING` or `FLOAT` → `INT`; `STRING` or `INT` → `FLOAT`) |
| `type()` | `INT` | Runtime particle-type code (see [particle types](https://aerospike.com/docs/database/manage/xdr/filters#ship-records-by-bin-type)) — bare bin only (`$.bin.type()`) |

There is no separate `getValues()` terminal: the implicit get (no terminal written) already returns matched values, so a dedicated values-only terminal would be redundant.

Restrictions:

-   `getKeys()` / `getKeyValues()` on a bare single-key navigation without multi-select context is a parse error.
-   `getIndexes()` / `getRanks()` / `getMaps()` are not valid on wildcard, filter, or inner-multi-select paths (deferred), for example `$.m.*.x.getRanks()`.
-   `getMaps()` requires a leaf map range or list selector; a list-only selector has no keys to return as a map (parse error).
-   `type()` applies to `$.bin` only, not nested paths (`$.bin.seg.type()` is a parse error). `exists()` accepts both a bare bin (`$.bin.exists()`) and a nested path (`$.bin.seg.exists()`).
-   `toInt()` / `toFloat()` require the receiver type to be known at parse time. If the bin is not already pinned elsewhere in the expression, attach `:INT`, `:FLOAT`, or `:STRING` on the path before the call, for example `$.bin:STRING.toInt() > 12`.

Result shapes (multi-select):

| Terminal | Shape |
| --- | --- |
| Implicit get | Flat list of values |
| `getKeyValues()` | Flat list of alternating keys and values |
| `getMaps()` | Single map (key → value) |
| `getTree()` | Nested map preserving path structure |

## Path write terminals

Return the modified collection unless noted. Selectors `{…}` / `[…]` attach to the path before the terminal, for example `$.m.{@k}.remove()`, not `$.m.remove({@k})`.

### Map writes

| Function | Description |
| --- | --- |
| `setTo(value)` | Upsert key from path navigation |
| `update(value)` | Update only — fail if key missing |
| `insert(value)` | Create only — fail if key exists |
| `add(amount)` | Numeric delta on map value |
| `putItems(items)` | Bulk upsert from `MAP` — mirrors `setTo` |
| `insertItems(items)` | Bulk create-only from `MAP` — mirrors `insert`; fails if any key exists |
| `updateItems(items)` | Bulk update-only from `MAP` — mirrors `update`; fails if any key is missing |
| `remove()` | Remove matched entries |
| `clear()` | Clear entire map |

::: caution
`clear()` irreversibly removes every element of the targeted map or list bin. It is not scoped by any selector — confirm the path targets the intended collection before using it against production records.
:::

```plaintext
$.m:MAP.putItems({a: 1, b: 2})       /* upsert both keys */

$.m:MAP.insertItems({a: 1, b: 2})    /* fails if 'a' or 'b' already exists */

$.m:MAP.updateItems({a: 1, b: 2})    /* fails if 'a' or 'b' is missing */
```

::: caution
Without `:PARTIAL`, a bulk write (`putItems()`, `insertItems()`, `updateItems()`, or the list `appendItems()` / `insertItems()` below) fails the entire call atomically on the first per-key or per-element error — no partial writes are applied.
:::

`putItems()`, `insertItems()`, and `updateItems()` also accept the `:NO_FAIL` and `:PARTIAL` postfix flags to tolerate per-key failures instead of failing the whole call; see [Postfix flags](https://aerospike.com/docs/develop/client/sdk/concepts/ael/reference/control-and-flags#postfix-flags).

### List writes

| Function | Description |
| --- | --- |
| `append(value)` / `appendItems(items)` | Append element(s) |
| `insert(value)` / `insertItems(items)` | Insert at index from path |
| `setTo(value)` | Overwrite at index |
| `add(amount)` | Increment at index |
| `remove()` / `clear()` | Remove matched elements / clear list |
| `sort()` | Sort list; optional `:DROP_DUPS` postfix |

List write terminals also accept `:ADD_UNIQUE` and `:NO_FAIL` where applicable; see [Postfix flags](https://aerospike.com/docs/develop/client/sdk/concepts/ael/reference/control-and-flags#postfix-flags).

Bulk symmetry: map `putItems` / `insertItems` / `updateItems` mirror single-key `setTo` / `insert` / `update`. List `appendItems` / `insertItems` mirror `append` / `insert`. List `setTo` / `add` at a path index mirror map single-key `setTo` / `add` (lists have no map-style `update`).

_Path addressing (maps vs. lists)_: write verbs contain _what_ is being operated on, and the path preceeding the verb contains _where_ it goes. Maps are keyed: the path names one key (or the whole map for bulk); bulk keys and values live in the map argument. Lists are indexed: `insert`, `insertItems`, `setTo`, and `add` take the index from path navigation (for example `$.l.[i].…`); the argument holds value(s) only. The index is never a function parameter.

|  | Address in path | Payload in `()` |
| --- | --- | --- |
| Map single-key | key (for example `$.m.k.…`) | value / amount |
| Map bulk | whole map (`$.m.…`) | map of keys → values |
| List at index | index (for example `$.l.[i].…`) | value / list of values |
| List bulk append | whole list (`$.l.…`) | list of values |

`insertItems` overload: same verb, different path shape. `$.m.insertItems({…})` is whole-map bulk create (no index leaf); `$.l.[0].insertItems([…])` is positional bulk insert at the path index, analogous to `$.l.[0].insert(value)`. `appendItems` is the list bulk op at container scope (append at end; no index leaf), parallel to map bulk ops on `$.m.…`.

Name disambiguation: `append(value)` on a `LIST` receiver appends a list element. String concatenation uses the `+` operator, not a string method — see [Arithmetic](https://aerospike.com/docs/develop/client/sdk/concepts/ael/reference/operators#arithmetic).

### Path modify and remove

| Function | Description |
| --- | --- |
| `modify(expr)` | Transform each matched element using `@`; result replaces element |
| `remove()` | Delete matched elements; no-op if none match |

::: caution
Inverted selection uses the `!` prefix on selectors — for example, `{!a:c}.remove()` deletes everything _except_ the range `a:c`, not the range itself. Confirm the `!` is intentional before using an inverted selector with `remove()` against production data.
:::

Create-order suffixes: `modify()` and `remove()` never accept create-order flags (see [Collection create-order suffixes](https://aerospike.com/docs/develop/client/sdk/concepts/ael/reference/paths#collection-create-order-suffixes)). For absent-path tolerance on those terminals, use `:NO_FAIL`.

Selector placement: `{…}` / `[…]` selectors attach to the path before the terminal, for example `$.m.{@k}.remove()`, not `$.m.remove({@k})`. Terminal-argument selector forms are not part of AEL; dynamic selection in selectors waits for path-parameter `(expr)` support (see [Parenthesised expressions](https://aerospike.com/docs/develop/client/sdk/concepts/ael/reference/paths#parenthesised-expressions)).

## Next steps

-   [String, BLOB, and HLL functions](https://aerospike.com/docs/develop/client/sdk/concepts/ael/reference/type-functions) — method-style functions on typed receivers
-   [Control structures and postfix flags](https://aerospike.com/docs/develop/client/sdk/concepts/ael/reference/control-and-flags) — `:NO_FAIL`, `:PARTIAL`, and more
-   [Operators](https://aerospike.com/docs/develop/client/sdk/concepts/ael/reference/operators) — comparison, logical, and arithmetic operators