Aerospike Expression Language (AEL)¶
AEL lets you write Aerospike filter expressions as strings.
Pass an AEL string to .where() on any query or write builder.
stream = await session.query(users).where("$.age > 18").execute()
Syntax Reference¶
Bin Access¶
Prefix bin names with $:
$.age
$.name
$.settings
Bin names accept a wider character set than plain identifiers:
$.name@host # @ is permitted in bin names
$.@attr # leading or trailing @
$."my-bin" # quoting allows otherwise-illegal characters (-, space, $, ...)
$.'my bin' # single quotes work too
$.true # reserved keywords are valid bin names
$.when # so are keywords like 'when', 'and', 'or', 'let', etc.
The substring null (case-insensitive) is reserved and rejected: $.null,
$.my_null_bin, and $."NULL" all raise AelParseException at parse time.
Comparison Operators¶
$.age == 30
$.age != 30
$.age > 18
$.age >= 18
$.age < 65
$.age <= 65
Logical Operators¶
$.age > 18 and $.status == "active"
$.role == "admin" or $.role == "superadmin"
not $.deleted
Arithmetic¶
$.price * $.quantity > 1000
$.score + $.bonus >= 100
$.total - $.discount > 0
$.value % 2 == 0
$.base ** 2 > 100
Arithmetic functions:
abs($.balance) > 100
ceil($.rating)
floor($.rating)
log($.value)
pow($.base, 2)
max($.a, $.b)
min($.a, $.b)
Bitwise Operators¶
$.flags & 0xFF
$.mask | 0x01
$.value ^ 0xAA
~$.mask
$.bits << 4
$.bits >> 2
$.bits >>> 2
Type Casting¶
$.count.asFloat() > 3.14
5.asFloat()
3.14.asInt()
String Values¶
Use double or single quotes:
$.name == "Alice"
$.name == 'Alice'
List Membership (IN)¶
$.status in ["active", "pending", "review"]
"gold" in $.tiers
CDT Paths¶
Access nested data with bracket notation:
$.settings.["theme"] == "dark"
$.scores.[0] > 90
$.matrix.[0].[1] == 42
$.users.["alice"].age > 30
Map keys can be typed at parse time:
$.bin.42 == 100 # integer map key (decimal)
$.bin.0xff == 100 # integer map key (hex)
$.bin.0b101 == 100 # integer map key (binary)
$.bin.+5 == 100 # signed integer map key
$.bin.-3 == 100
$.bin."42" == "x" # string map key (quoting forces string type)
$.bin.{1-5} == 100 # integer key range
$.bin.{1,2,3} == 100 # integer key list
A digit-only segment after the dot ($.bin.42) becomes an integer map key;
quote it ($.bin."42") to force string interpretation. The two compile to
distinct expressions and match different keys at runtime.
CDT Functions¶
$.scores.count() > 5
$.tags.count() == 0
GeoJSON¶
Compare a GeoJSON bin to a literal value with geoCompare(a, b). Either side
can be a bin path or a geoJson('...') literal — pick whichever reads more
naturally. The match semantics are server-side GEO2DSPHERE: a Point matches
any AeroCircle or Polygon containing it, and vice versa.
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 when referenced inside
geoCompare(...); an explicit cast like $.loc.get(type: GEO) is accepted
but not required.
HyperLogLog¶
Seven read-side HLL path functions are available on HLL bins. Each operates on
$.binName as the receiver:
$.h.hllCount() > 1000000
$.h.hllDescribe() == [14, 0]
$.h.hllMayContain(['alice', 'bob']) == 1
$.h.hllUnionCount(?0) > 50000
$.h.hllIntersectCount(?0) > 100
$.h.hllSimilarity(?0) >= 0.8
$.h.hllUnion(?0) == ?1
hllDescribe() returns a two-element list [index_bit_count, min_hash_bit_count];
the server reports 0 for a sketch without minhash (the -1 sentinel used
client-side to mean “inherit / no minhash” is normalized away on the wire).
The multi-sketch functions (hllUnion, hllUnionCount, hllIntersectCount,
hllSimilarity) take their multi-sketch argument in one of two shapes:
A single HLL bin reference —
$.a. The server treats a bare HLL value as an implicit single-element list, so$.h.hllUnionCount($.a)evaluates cleanly.A list-typed expression of HLL byte blobs — either an inline literal list
[?0, ?1]or a placeholder bound to a Pythonlist[bytes].
[$.a, $.b] (a list literal containing bin references) is not supported
— the server’s HLL ops can’t recursively evaluate scalar bin sub-expressions
inside a composed list. If you need to combine multiple bins in one
expression without pre-fetching, drop down to the programmatic Exp.* API
or open multiple bin-pair queries.
Write-side AEL (hllInit, hllAdd) is not currently supported — the
existing grammar allows at most one path function per path, and chained
write-then-read forms require a grammar refactor that’s better aligned with
the upcoming server-side AEL design. Use the builder API
(session.upsert(key).bin("h").hll_init(HllConfig.of(14))) for writes
today; AEL is read-only for HLL until then.
Hex and Binary Literals¶
$.flags == 0xFF
$.mask == 0b10101010
Variables (let/then)¶
Bind intermediate values:
let $total = $.price * $.qty then $total > 1000
Placeholders¶
Use ?0, ?1, etc. for parameterized queries:
from aerospike_sdk import parse_ael
expr = parse_ael("$.age > ?0 and $.status == ?1", 18, "active")
Unknown and Error¶
The unknown and error keywords compile to a sentinel that the server
treats as an evaluator-unknown result — useful as a when action when no
sensible value can be returned:
when ($.role == "admin" => $.tier, default => unknown)
error is an alias for unknown and produces the same expression. Both
short-circuit any enclosing comparison or logical operator.
Auto Index Discovery¶
When a secondary index exists on a bin referenced in the AEL expression,
the client automatically generates an optimal secondary index Filter
alongside the FilterExpression. This is transparent — no code changes needed.
To influence index selection, use QueryHint:
from aerospike_sdk import QueryHint
stream = await (
session.query(users)
.where("$.age > 25 and $.city == 'NYC'")
.with_hint(QueryHint(index_name="age_idx"))
.execute()
)
Programmatic Expressions¶
For cases where a string AEL expression is insufficient, use the Exp builder
or raw FilterExpression from aerospike_async:
from aerospike_sdk import Exp
expr = Exp.and_([
Exp.gt(Exp.int_bin("age"), Exp.int_val(18)),
Exp.eq(Exp.string_bin("status"), Exp.string_val("active")),
])
stream = await session.query(users).where(expr).execute()
Path Expressions (Server 8.1.1+)¶
Path expressions — select_by_path / modify_by_path, the SelectFlag and
ModifyFlag return/modify flag enums, CTX.all_children() /
CTX.all_children_with_filter(), and the loop-variable family
(FilterExpression.int_loop_var, .string_loop_var, .map_loop_var, etc.)
— are not yet surfaced through the AEL string grammar. Use the low-level
aerospike_async types directly:
from aerospike_async import (
CTX,
CdtOperation,
FilterExpression,
LoopVarPart,
ModifyFlag,
SelectFlag,
)
in_stock = FilterExpression.eq(
FilterExpression.map_loop_var(LoopVarPart.VALUE),
FilterExpression.bool_val(True),
)
op = CdtOperation.select_by_path(
"store",
SelectFlag.VALUE,
[CTX.map_key("books"), CTX.all_children_with_filter(in_stock)],
)
These constructs require Aerospike Server 8.1.1 or newer. A dedicated AEL surface is deferred until the DSL shape stabilizes across clients.