Chapter 20. Portable Named-Function Dialect

Every MobilityDB operator is also callable as a bare named function. A query written with named functions only, using no operator symbols, is portable: the same SQL runs unchanged across the MobilityDB ecosystem — the database engines MobilityDB (PostgreSQL), MobilityDuck (DuckDB) and MobilitySpark (Apache Spark), and the streaming engines — several of which cannot register PostgreSQL operator symbols. Each named alias reuses the operator's own implementation, so it returns exactly the same result as the operator.

This chapter is the canonical, ecosystem-wide reference for that mapping. An engine that does not accept a given operator can look it up here and use the function form instead. For example, DuckDB does not accept any operator containing # (it reserves # for positional column references), so on MobilityDuck the temporal comparisons (#=, …) are written tEq, … and the time-position tests (<<#, #>>, &<#, #&>) are written before, after, overbefore, overafter, while the operators it does accept may be used directly.

The spatial-relationship functions (eIntersects, aIntersects, eTouches, eDwithin, …) and the restriction functions (atTime, atGeometry, atValue, …) are only ever named functions (they have no operator). The B-tree ordering comparator is likewise the named function cmp(a, b), which returns -1, 0 or 1 and has no operator. Every operator maps to a bare name as follows; for the arithmetic and set-theoretic operators the function name carries the operand's type-family prefix (set, span or spanset, and t for temporal numbers).

CategoryOperatorPortable function
Topology&&overlaps(a, b)
 @>contains(a, b)
 <@contained(a, b)
 -|-adjacent(a, b)
Time position<<#before(a, b)
 #>>after(a, b)
 &<#overbefore(a, b)
 #&>overafter(a, b)
Space, X axis<<left(a, b)
 >>right(a, b)
 &<overleft(a, b)
 &>overright(a, b)
Space, Y axis<<|below(a, b)
 |>>above(a, b)
 &<|overbelow(a, b)
 |&>overabove(a, b)
Space, Z axis<</front(a, b)
 />>back(a, b)
 &</overfront(a, b)
 /&>overback(a, b)
Temporal comparison#=tEq(a, b)
 #<>tNe(a, b)
 #<tLt(a, b)
 #<=tLe(a, b)
 #>tGt(a, b)
 #>=tGe(a, b)
Distance<->tDistance(a, b)
 |=|nearestApproachDistance(a, b)
Same~=same(a, b)
Traditional comparison=eq(a, b)
 <>ne(a, b)
 <lt(a, b)
 <=le(a, b)
 >gt(a, b)
 >=ge(a, b)
Ever comparison?=eEq(a, b)
 ?<>eNe(a, b)
 ?<eLt(a, b)
 ?<=eLe(a, b)
 ?>eGt(a, b)
 ?>=eGe(a, b)
Always comparison%=aEq(a, b)
 %<>aNe(a, b)
 %<aLt(a, b)
 %<=aLe(a, b)
 %>aGt(a, b)
 %>=aGe(a, b)
Arithmetic (temporal numbers)+tAdd(a, b)
 -tSub(a, b)
 *tMul(a, b)
 /tDiv(a, b)
Union (set / span / span set)+setUnion / spanUnion / spansetUnion (a, b)
Intersection (set / span / span set)*setIntersection / spanIntersection / spansetIntersection (a, b)
Difference (set / span / span set)-setMinus / spanMinus / spansetMinus (a, b)

For example, the bounding-box overlap t1.trip && t2.trip is written portably as overlaps(t1.trip, t2.trip), and the temporal-before test p1.period <<# p2.period as before(p1.period, p2.period).