Methods
Methods are selected at compile time. A backend may support only some methods.
Parsing a method call does not imply that it can be compiled. Calls on a
statically incompatible receiver are compile-time errors. A runtime type that
matches no applicable, enabled signature yields null, but every statically
applicable signature on a union or Any must be enabled at compile time. A
value-only null receiver likewise has no type family to select a single
signature, so all statically applicable candidates must be enabled. To permit
only selected families, use the explicit value:[Type] guard described in
syntax and types.
The type() method remains an ordinary string-valued runtime method and has no
special flow-narrowing behavior.
The catalog below records the supplied signatures. any... and string...
denote variadic arguments; brackets in a signature denote an optional argument.
In query source, parentheses may be omitted when a method call supplies no
arguments, including when all declared arguments are optional. Required
arguments still produce a compile-time error when omitted. Method names in
prose and signatures in this catalog retain parentheses; sample query syntax
may omit parentheses when no argument is supplied. func(T) -> U is
explanatory callback notation, not HFF source syntax.
Counts, timestamp components, and
duration:nanoseconds() return int64. Other duration unit conversions return
float64; round(), ceil(), and floor() preserve the input numeric subtype.
Any value
| Signature | Purpose |
|---|---|
any:type() -> string |
Runtime type name: bool, number, string, list, object, bytes, timestamp, duration, or null |
any:json() -> string |
JSON serialization |
any:string() -> string |
String conversion |
any:in(any...) -> bool |
Membership among the arguments |
any:exists() -> bool |
True for any present, non-null value |
On a null receiver, type(), json(), and zero-argument string() return
"null". The timestamp-specific string(tz) overload still requires a zone.
The zero-argument any:string() formats timestamps in UTC; the required-zone
timestamp:string(tz) overload is a separate method and capability.
exists() returns false for null or guarded no-value, and true for every other
value, including false, zero, and empty collections. It deliberately observes
presence rather than truthiness. On guarded no-value, type(), json(), and
zero-argument string() propagate no-value unchanged. Other methods propagate
null and guarded no-value, subject to predicate callback conversion described
below.
in() requires at least one argument and tests membership among its arguments.
string() leaves strings unquoted, formats booleans and numbers in ordinary
lowercase/decimal notation, emits compact JSON for lists and objects, standard
base64 for bytes, UTC RFC 3339 for timestamps, and compound h/m/s for durations
with fractional seconds representing nanoseconds. json() produces compact
JSON and represents bytes, timestamps, and durations as these strings.
Object keys are sorted in code-point order. Non-JSON type information is not
restored automatically by fromjson(). Float64 values retain a decimal point
or exponent marker to preserve the numeric subtype. Their exact decimal
spelling and signed-zero text follow the selected backend's float.format
Profile detail. Integer values use integer decimal spelling. Zero duration
is written 0s.
Booleans and numbers
| Signature | Purpose |
|---|---|
bool:and(bool) -> bool |
Logical AND |
bool:or(bool) -> bool |
Logical OR |
bool:not() -> bool |
Logical NOT |
number:round() -> number |
Round to an integer value |
number:ceil() -> number |
Round upward |
number:floor() -> number |
Round downward |
For Int, round(), ceil(), and floor() are identity operations. Float
round(), ceil(), and floor() follow the selected backend's
float.rounding Profile detail. Numbers distinguish int64 and float64;
rounding methods preserve the numeric subtype.
Strings and bytes
| Signature | Purpose |
|---|---|
string:len() -> number |
String length |
string:contains(string...) -> bool |
All supplied substrings are present; at least one argument |
string:matches(string) -> bool |
RE2 matching |
string:startswith(string) -> bool |
Prefix test |
string:endswith(string) -> bool |
Suffix test |
string:lower() -> string |
Lowercase conversion |
string:upper() -> string |
Uppercase conversion |
string:timestamp() -> timestamp |
Parse an RFC 3339 string |
string:duration() -> duration |
Parse a duration string |
string:bool() -> bool |
Parse a boolean |
string:number() -> number |
Parse a number |
string:fromjson() -> any |
Parse JSON |
string:concat(string) -> string |
Concatenation |
string:frombase64() -> bytes |
Decode base64 |
string:frombase64r() -> bytes |
Decode unpadded standard base64 |
bytes:len() -> number |
Byte count |
bytes:base64() -> string |
Encode base64 |
bytes:base64r() -> string |
Encode unpadded standard base64 |
String length counts Unicode code points. Case conversion uses locale-independent
Unicode simple mappings, without multi-code-point expansions;
strings are not implicitly normalized. contains() requires at least one
argument and tests every supplied substring. matches() uses RE2 substring
matching; anchors select full-string matching. Invalid patterns and invalid
conversions yield null. HFF's regex reference syntax follows RE2 except the
byte escape \C, which is excluded and therefore invalid. A backend may
support a smaller regex subset only when its support matrix or compatibility
profile documents the restriction. base64() and frombase64() use the
standard alphabet with required padding. base64r() and frombase64r() use
the same standard alphabet without padding; an empty string represents empty
bytes. The encoders emit canonical output for their respective forms. The
decoders reject URL-safe characters, embedded CR/LF, and non-canonical pad
bits. Invalid base64 returns null, and null propagates through the conversion
methods.
bool() accepts exactly true or false. number() accepts JSON numeric
spelling and follows literal subtype selection. Neither trims surrounding
whitespace. fromjson() rejects duplicate keys and out-of-range numbers with
a null result, even when its receiver is a constant string. JSON parsing and
canonical JSON serialization use the dedicated MaxJSONDepth resource limit:
64 by default, counting the JSON root at depth one and incrementing for each
nested array or object. It applies to fromjson(), json(), and list/object
string() serialization. A scalar string() value has depth one. Exceeding
the limit aborts evaluation with a limit error rather than returning null or a
partial serialization.
Lists
| Signature | Purpose |
|---|---|
list:size() -> number |
Element count |
list:count() -> number |
Element count |
list:count(func(T) -> bool) -> number |
Count elements whose callback result converts to true |
list:filter(func(T) -> bool) -> list |
Keep elements whose callback result converts to true |
list:sum() -> number |
Sum numeric elements |
list:sum(func(T) -> number) -> number |
Sum numeric callback results |
list:avg() -> float |
Average numeric elements |
list:avg(func(T) -> number) -> float |
Average numeric callback results |
list:min() -> number |
Smallest numeric element |
list:min(func(T) -> number) -> number |
Smallest numeric callback result |
list:max() -> number |
Largest numeric element |
list:max(func(T) -> number) -> number |
Largest numeric callback result |
list:distinct() -> list<T> |
Stable deduplication by HFF equality |
| `list:find(func(T) -> bool) -> T | null` |
list:has(number) -> bool |
Index-presence test |
list:contains(any...) -> bool |
All supplied elements are present; at least one argument |
list:unique() -> bool |
Whether every element is distinct |
list:sort() -> list |
Sorted list |
list:concat(list) -> list |
Concatenation |
list:any(func(T) -> bool) -> bool |
At least one matching element |
list:all(func(T) -> bool) -> bool |
Every element matches |
list:map(func(T) -> U) -> list |
Element transformation |
list:flatmap(func(T) -> list) -> list |
Transformation followed by flattening |
list:foldl(A, func(A, T) -> A) -> A |
Left fold, callback arguments (acc, x) |
list:foldr(A, func(T, A) -> A) -> A |
Right fold, callback arguments (x, acc) |
List element count uses size(), including implicit boolean conversion.
count() without a callback returns the element count. With a predicate
callback, it counts elements whose callback result converts to true. The
callback is evaluated for each element; guarded no-value converts to false for
that element. An empty list counts as zero. A null receiver yields null.
filter(predicate) retains the original elements whose callback result
converts to true, in their original order; it does not transform them. Guarded
no-value converts to false for that element. Filtering an empty list returns
an empty list, and a null receiver yields null.
sum() accepts a list of numeric values or one callback that projects each
element to a numeric value. Null values and guarded no-value callback results
are skipped. A nonnumeric runtime value makes the result null. An empty list or
one with no contributing values sums to Int zero. If every contribution is Int,
the exact total is returned when it fits int64; otherwise the result is null. If
any contribution is Float, the result is Float and its arithmetic follows the
backend's declared Float model; a non-finite result is null. A null receiver
yields null. The zero-argument method requires
method:list.sum; the callback form also requires Callbacks. Cancellation or
a resource-limit error aborts instead of returning a partial sum.
avg() accepts numeric list elements or one callback that projects each
element to a number. It skips null values and guarded no-value callback results.
A nonnumeric runtime value, an empty list, or a list with no contributing
values returns null. The result is always Float. Local accumulates Int values
exactly until a Float contribution occurs, then uses Go float64 addition in
list order; an all-Int total is converted to Float before division. The total
is divided by the number of contributing values. A non-finite result returns
null. A null receiver yields null. The zero-argument method requires
method:list.avg; the callback form also requires Callbacks.
PostgreSQL JSON/JSONB list bindings use a backend-native numeric aggregate for
avg() when every numeric subtype is recoverable. Float contributions are
converted through round-trip-safe Float text before numeric aggregation, then
the final average is checked when converted to Float. This can differ from
Local's accumulation order and requires extra_float_digits > 0; HFF does not
change the database setting.
PostgreSQL relation bindings can also use avg() with a numeric callback
projection when bound values preserve their numeric subtypes. Averaging does
not require a unique relation order unless a nested operation observes order.
min() and max() select from numeric list elements or numeric callback
results. Null values and guarded no-value callback results are skipped. A
nonnumeric runtime value makes the result null; an empty list or one with no
contributing values also returns null. Mixed Int and Float values are compared
exactly without rounding either value first. The selected original value is
returned with its original numeric subtype (the numeric callback result when a
projection callback is used), and the first value wins a tie. A null receiver
yields null. The zero-argument forms require method:list.min
and method:list.max respectively; callback forms also require Callbacks.
PostgreSQL JSON/JSONB lists support these methods when stored values and
callback results retain recoverable numeric subtypes, including dynamic values
whose subtype can be recovered. JSONB requires version-1 numeric type metadata
when storage loses the Int/Float distinction. Bound relations support numeric
callback projections; Int-only or null-only projections do not require a
unique relation order, while Float or mixed projections do because ties can
retain distinct representations.
distinct() returns a new list containing the first occurrence of each value,
in input order, using structural HFF equality. Null is an ordinary value and
appears at most once. Mixed Int and Float values compare by their exact numeric
value; when equal values have different subtypes or Float representations, the
first original value is retained, including its signed zero. A null receiver
yields null and a guarded receiver propagates no-value. The method requires
method:list.distinct; when an element type
can contain lists or objects, it also requires the corresponding structural
equality capabilities, checked recursively as for unique().
find(predicate) returns the original first element whose callback result
converts to true; it does not return the callback result. It evaluates elements
from left to right and stops at the first match. Guarded no-value from the
predicate converts to false. An empty list or no match returns null, as does a
null receiver; finding an element whose value is null also returns ordinary
null. A guarded receiver propagates no-value. The method requires
method:list.find and Callbacks; nested callback limits and capability rules
apply when the predicate contains nested callbacks.
has() supports the same negative indices as element access. unique() returns
whether every element is distinct using structural equality. has() returns
false for an out-of-range integer-valued index, and null for a non-integer index. sort() is stable
and requires a shared orderable type; mixed numeric subtypes are allowed.
An invalid runtime sort domain yields null. Methods never mutate the receiver.
any() and all() evaluate predicate callbacks left-to-right with ordinary
boolean conversion of callback results; the displayed bool result signature
includes that contextual conversion. They short-circuit when their answer is
known. Empty lists return false for any and true for all. Null receivers yield
null. Guarded no-value from a predicate callback converts to false for that
element and does not invalidate other elements. A cancellation still aborts the
call; it is never a false predicate result.
count(predicate) and filter(predicate) use the same callback syntax and
ordinary boolean conversion, but inspect the whole list because their result
depends on every element. Callback order is left to right. Cancellation or a
resource-limit error aborts the method rather than returning a partial count or
list.
items:any(x=>x.price>10)
items:all(x=>x.active)
Callbacks use x=>expr or (acc,x)=>expr. Parameter types are inferred, and
callbacks may reference outer schema variables. There are no general function
declarations or recursion. map(), flatmap(), and foldl() visit elements
left-to-right; foldr() visits right-to-left. The accumulator type A may differ
from the element type T. An empty fold returns the initial accumulator.
Callbacks may nest. Inner callbacks can capture unshadowed outer callback
parameters; a nested parameter may shadow an outer name. Nested callback
expressions require the NestedCallbacks capability, which depends on
Callbacks. MaxCallbackDepth defaults to 64; the outermost callback has depth
- AND and OR use the ordinary left-to-right short-circuit semantics in callback
bodies. Compilation also bounds semantic work with
MaxCompileSteps; local execution separately charges evaluated callback work to its per-matchMaxStepsbudget.
map() preserves null callback results as list elements. A non-list result in
flatmap() yields null for the entire method. Guarded no-value in a callback
propagates to the entire method; failed elements are never silently skipped.
Objects
| Signature | Purpose |
|---|---|
object:size() -> number |
Field or entry count |
object:has(string) -> bool |
Field or key presence |
object:get(string) -> any |
Field or key lookup |
object:keys() -> list |
Keys |
object:values() -> list |
Values |
object:items() -> list |
Key/value entries |
has() tests presence, including keys whose value is null. keys(), values(), and items()
enumerate in code-point key order. Each item is a two-element list [key, value];
values and items follow the same ordering as keys.
get() returns null for a missing key.
Timestamps
| Signature | Purpose |
|---|---|
timestamp:year([tz: string]) -> number |
Calendar year |
timestamp:month([tz: string]) -> number |
Calendar month |
timestamp:dayofmonth([tz: string]) -> number |
Day of month |
timestamp:dayofyear([tz: string]) -> number |
Day of year |
timestamp:hours([tz: string]) -> number |
Hour component |
timestamp:minutes([tz: string]) -> number |
Minute component |
timestamp:seconds([tz: string]) -> number |
Second component |
timestamp:milliseconds([tz: string]) -> number |
Millisecond component |
timestamp:string(tz: string) -> string |
String rendering in a time zone |
timestamp:after(timestamp) -> bool |
Later-than test |
timestamp:before(timestamp) -> bool |
Earlier-than test |
timestamp:add(duration) -> timestamp |
Time plus duration |
The component methods accept an optional time-zone argument and default to UTC.
timestamp:string(tz) requires an explicit time zone. Timestamps have nanosecond
precision and format as RFC 3339, omitting trailing fractional zeros. Invalid or
unrepresentable timestamps yield null. Years range from 0000 through 9999. Month, day-of-month,
and day-of-year use one-based calendar numbering. Hours range from 0 through 23,
minutes and seconds from 0 through 59, and the millisecond component from 0
through 999. Leap seconds are not supported, and input fractional seconds have
at most nine digits. Methods never depend on the machine's local time zone.
Durations
| Signature | Purpose |
|---|---|
duration:hours() -> number |
Duration in hours |
duration:minutes() -> number |
Duration in minutes |
duration:seconds() -> number |
Duration in seconds |
duration:milliseconds() -> number |
Duration in milliseconds |
duration:microseconds() -> number |
Duration in microseconds |
duration:nanoseconds() -> number |
Duration in nanoseconds |
Duration strings support these suffixes:
| Suffix | Unit |
|---|---|
h |
Hour |
m |
Minute |
s |
Second |
ms |
Millisecond |
us |
Microsecond |
ns |
Nanosecond |
The zero string "0", negative values such as "-1h", fractional values such
as "-23.4s", and compound values such as "1h34us" are supported.
Units larger than an hour, including days and weeks, are not supported. This
avoids ambiguity involving calendar boundaries, time zones, locales, and leap
seconds. Fixed-duration arithmetic must not implicitly become calendar arithmetic.
Durations are signed int64 nanoseconds. Unit methods return total amounts,
not calendar components; overflow yields null. A leading plus or minus sign and
compound fractional units are allowed, including +1h30m and .5h. Only the
listed suffixes are supported. Fractional components are added exactly before checking the total against
nanosecond precision and int64 bounds. Values not exactly representable as nanoseconds
yield null rather than being truncated.
Time zones
Accepted time zone forms are UTC, a named IANA or Joda-Time time zone, or a fixed
offset of the form +HH:MM or -HH:MM:
timezone = "UTC" / longtz / fixedtz
longtz = <an IANA or Joda-Time time zone name>
fixedtz = ("+" / "-") digit digit ":" digit digit
digit = [0-9]
Examples include "Asia/Tokyo", "America/New_York", and "+09:00".
Fixed offsets range through +23:59 and -23:59. Engines identify their time
zone data versions and explicitly supported Joda-Time aliases. Unknown or
unavailable names yield null. A backend must document supported zone data and
reject features whose HFF semantics it cannot preserve.
The local backend accepts named-zone data through local.TimeZoneProfile,
which contains a nonempty version label, a map of zone names to TZif bytes, and
optional aliases. local.WithTimeZones or local.Config.TimeZones supplies the
profile to local.Build. Build copies and parses the data into the filter; a
later Match performs no filesystem or environment lookup. Aliases point
directly to a zone in the profile or to UTC; alias chains are not accepted. UTC
and fixed offsets are built in and cannot be overridden by a profile. Invalid
profiles are configuration errors. Filter.TimeZoneVersion() returns the
profile's version label, or the empty string when only UTC and fixed offsets
are available.