Types

FeatureQL implements a strong type system where each feature must have a specific data type defined at creation time.

This type-safe approach ensures data consistency across feature definitions.

Supported types

The following scalar and complex canonical types are supported:

CategoryTypesAliases
IntegerBIGINTINT64
MEDIUMINTINT32
SMALLINTINT16
TINYINTINT8
DecimalDECIMAL
Floating PointFLOATFLOAT32
DOUBLEFLOAT64
StringVARCHAR
BooleanBOOLEAN
TemporalTIMESTAMP
DATE
INTERVAL
DocumentJSON
ComplexARRAYLIST
ROWSTRUCT

Please note that FeatureQL does not support the MAP type. You can instead use an ARRAY of ROWs, possibly defined with an INDEX.

Type inference

FeatureQL uses automatic type inference, eliminating the need for explicit type declarations in pure transformations.

Types are determined based on:

  • Literal detection

    • Types are automatically derived from literal value formats
    • Example: 'Hello' is inferred as a VARCHAR or TIMESTAMP '2024-01-09' is inferred as TIMESTAMP
  • Transformed features

    • Functions and operators have a deterministic response type based on their input types
    • Example: 1 + 2 is inferred as a BIGINT or 1e0 + 2e0 is inferred as DOUBLE
  • No type coercion is performed between types

    • When mixing types, you must use type casting.
    • Example: 1 + 2e0 is not allowed because 1 is a BIGINT and 2e0 is a DOUBLE
    • Example: 1::DOUBLE + 2e0 is allowed because 1 is now casted to DOUBLE

Explicit type declaration

Explicit type declarations are required only in two scenarios:

  • Input features

    • When defining features that serve as input parameters using the INPUT() function.
    • Example: INPUT(BIGINT) AS CUSTOMER_ID
  • External source mappings

    • When mapping features to external data sources using EXTERNAL_xxx() functions.
    • Example: EXTERNAL_SQL(<query> ON <conditions> AS ROW(BIGINT))

Entity annotation

Entities represent the core objects in your data model. Declare them using the ENTITY() function.

By convention, use plural forms for entity names.

SELECT
    CUSTOMERS := ENTITY(),
    STORES := ENTITY(),
    ORDERS := ENTITY()
sql

Features representing entity ids can be linked to their entities through type annotation. This creates relationships between the features holding the entity id and the entity this id represent.

SELECT
    CUSTOMERS := ENTITY(),
    CUSTOMER_ID := INPUT(BIGINT#CUSTOMERS)
sql

Feature ids support BIGINT, VARCHAR and TIMESTAMP.

You can then represent a numeric identifier as a BIGINT, a UUIDs as VARCHAR, or a categorical attribute as a VARCHAR.

Type casting

FeatureQL supports two equivalent syntaxes for type conversion:

  1. Standard SQL syntax: CAST(<feature> AS <new_type>)
  2. PostgreSQL-style syntax with double colons (::)

Both methods achieve the same result, allowing you to choose the syntax that best fits your coding style or team conventions. The PostgreSQL-style syntax (::) is particularly useful when writing complex expressions as it reduces nested parentheses and has higher precedence than most operators.

Key points from the example:

  • CAST('123' AS BIGINT) and '123'::BIGINT produce identical results
  • The :: operator has higher precedence than string concatenation (||)
  • Type casting is essential when mixing different data types in expressions

Entity annotations in casting

When working with entity-annotated types, you can control how entity relationships are preserved during casting:

This example demonstrates:

  • Removing entity annotation: ENTITY1_ID::BIGINT strips the entity relationship
  • Changing entity annotation: ENTITY1_ID::BIGINT#ENTITY2 reassigns to a different entity
  • Preserving relationships: Mathematical operations require casting to remove entity annotations first, then re-applying them if needed

Type inspection

To inspect the type of a feature, you can use the TYPEOF() function that returns the type of a feature as a feature value.

The example shows the difference between:

  • FeatureQL types: The canonical type used internally (BIGINT)
  • Backend SQL types: The actual type in the target database (INTEGER, Int64, INT64)

This distinction is important when debugging type-related issues to understand at what level (translation or execution) the problem comes from.

Last update at: 2025/10/13 10:23:46