Chapter 18. 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. The named functions form one SQL dialect across these batch and stream SQL bindings, so each name must also be free in every engine: a quantified comparison therefore spells out its operation, as in eLessThan, since eLt would coincide with the function elt of Apache Spark and Apache Flink, and a position test carries the prefix of the class it compares, as in spanLeft, since left and right are functions of both engines. 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 tEqual, … and the time-position tests (<<#, #>>, &<#, #&>) are written under the prefix of the class compared, as spanBefore, spanAfter, spanOverbefore, spanOverafter between time spans, 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 named function 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 values), and for the position operators the prefix of the class compared: set, span or spanset for sets, spans and span sets, and for a box or a temporal value the type of its bounding box, tbox, stbox or tpcbox, a temporal Boolean, text or JSONB value taking span, the time span that bounds it.

CategoryOperatorPortable function
Topology&&overlaps(a, b)
 @>contains(a, b)
 <@contained(a, b)
 #@>contains(a, b)
 <@#contained(a, b)
 -|-adjacent(a, b)
Time position<<#setBefore / spanBefore / spansetBefore / tboxBefore / stboxBefore / tpcboxBefore (a, b)
 #>>setAfter / spanAfter / spansetAfter / tboxAfter / stboxAfter / tpcboxAfter (a, b)
 &<#setOverbefore / spanOverbefore / spansetOverbefore / tboxOverbefore / stboxOverbefore / tpcboxOverbefore (a, b)
 #&>setOverafter / spanOverafter / spansetOverafter / tboxOverafter / stboxOverafter / tpcboxOverafter (a, b)
Space, X axis<<setLeft / spanLeft / spansetLeft / tboxLeft / stboxLeft / tpcboxLeft (a, b)
 >>setRight / spanRight / spansetRight / tboxRight / stboxRight / tpcboxRight (a, b)
 &<setOverleft / spanOverleft / spansetOverleft / tboxOverleft / stboxOverleft / tpcboxOverleft (a, b)
 &>setOverright / spanOverright / spansetOverright / tboxOverright / stboxOverright / tpcboxOverright (a, b)
Space, Y axis<<|stboxBelow / tpcboxBelow (a, b)
 |>>stboxAbove / tpcboxAbove (a, b)
 &<|stboxOverbelow / tpcboxOverbelow (a, b)
 |&>stboxOverabove / tpcboxOverabove (a, b)
Space, Z axis<</stboxFront / tpcboxFront (a, b)
 />>stboxBack / tpcboxBack (a, b)
 &</stboxOverfront / tpcboxOverfront (a, b)
 /&>stboxOverback / tpcboxOverback (a, b)
Temporal comparison#=tEqual(a, b)
 #<>tNotEqual(a, b)
 #<tLessThan(a, b)
 #<=tLessEqual(a, b)
 #>tGreaterThan(a, b)
 #>=tGreaterEqual(a, b)
Distance<->tDistance(a, b)
 |=|nearestApproachDistance(a, b)
Distance (base / set / span / span set)<->distance / setDistance (a, b)
Grid distance (cell index)<->th3GridDistance(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?=eEqual(a, b)
 ?<>eNotEqual(a, b)
 ?<eLessThan(a, b)
 ?<=eLessEqual(a, b)
 ?>eGreaterThan(a, b)
 ?>=eGreaterEqual(a, b)
Always comparison%=aEqual(a, b)
 %<>aNotEqual(a, b)
 %<aLessThan(a, b)
 %<=aLessEqual(a, b)
 %>aGreaterThan(a, b)
 %>=aGreaterEqual(a, b)
Boolean (temporal bolean)&tAnd(a, b)
 |tOr(a, b)
 ~tNot(a)
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)
Concatenation (set / temporal)||setConcat / tConcat (a, b)
JSON field / element access->jsonbsetObjectField / tjsonbObjectField / jsonbsetArrayElement / tjsonbArrayElement (a, b)
 ->>jsonbsetObjectFieldText / tjsonbObjectFieldText / jsonbsetArrayElementText / tjsonbArrayElementText (a, b)
JSON key existence?jsonbsetExists / tjsonbExists (a, b)
 ?&jsonbsetExistsAll / tjsonbExistsAll (a, b)
 ?|jsonbsetExistsAny / tjsonbExistsAny (a, b)
JSON path@?jsonbsetPathExists / tjsonbPathExists (a, b)
 @@jsonbsetPathMatch / tjsonbPathMatch (a, b)
 #-jsonbsetDeletePath / tjsonbDeletePath (a, b)
Route identifier (network point)@=same_rid(a, b)
 @?contains_rid(a, b)
 ?@contained_rid(a, b)
 @@overlaps_rid(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 spanBefore(p1.period, p2.period).

Aggregate functions follow the same principle, and are listed in their own table below so that an engine can look up the machinery it needs without hunting through the prose. A SQL aggregate is assembled from up to three PostgreSQL machinery functions, each exposed under a canonical name built from the aggregate name and its role: a transition function <aggregate>Transition (always present), a combine function <aggregate>Combine used for parallel aggregation, and a final function <aggregate>Final. The combine and final roles are each optional: an aggregate whose transition state is already its result has no final function (for example extent), and an aggregate that does not support partial aggregation has no combine function (for example the append aggregates). The roles stay distinct and uniformly named so that every binding can reconstruct the aggregate from the catalog. The following table lists every aggregate and its machinery functions; a dash (—) marks a role the aggregate does not define.

CategoryTransitionCombineFinal
CounttCountTransitiontCountCombinetCountFinal
 wCountTransitionwCountCombinewCountFinal
SumtSumTransitiontSumCombinetSumFinal
 wSumTransitionwSumCombinewSumFinal
AveragetAvgTransitiontAvgCombinetAvgFinal
 wAvgTransitionwAvgCombinewAvgFinal
Minimum / maximumtMinTransitiontMinCombinetMinFinal
 tMaxTransitiontMaxCombinetMaxFinal
 wMinTransitionwMinCombinewMinFinal
 wMaxTransitionwMaxCombinewMaxFinal
BooleantAndTransitiontAndCombinetAndFinal
 tOrTransitiontOrCombinetOrFinal
CentroidtCentroidTransitiontCentroidCombinetCentroidFinal
Point cloudtnpointsTransitiontnpointsCombinetnpointsFinal
 tdensityTransitiontdensityCombinetdensityFinal
Bounding boxextentTransitionextentCombine
UnionsetUnionTransitionsetUnionCombinesetUnionFinal
 spanUnionTransitionspanUnionCombinespanUnionFinal
 spansetUnionTransitionspansetUnionCombinespansetUnionFinal
MergemergeTransitionmergeCombinemergeFinal
AppendappendInstantTransitionappendInstantFinal
 appendSequenceTransitionappendSequenceFinal