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:
Category | Types | Aliases |
---|---|---|
Integer | BIGINT | INT64 |
MEDIUMINT | INT32 | |
SMALLINT | INT16 | |
TINYINT | INT8 | |
Decimal | DECIMAL | |
Floating Point | FLOAT | FLOAT32 |
DOUBLE | FLOAT64 | |
String | VARCHAR | |
Boolean | BOOLEAN | |
Temporal | TIMESTAMP | |
DATE | ||
INTERVAL | ||
Document | JSON | |
Complex | ARRAY | LIST |
ROW | STRUCT |
Please note that FeatureQL does not support the MAP
type. You can instead use an ARRAY
of ROW
s, 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 aVARCHAR
orTIMESTAMP '2024-01-09'
is inferred asTIMESTAMP
Transformed features
- Functions and operators have a deterministic response type based on their input types
- Example:
1 + 2
is inferred as aBIGINT
or1e0 + 2e0
is inferred asDOUBLE
No type coercion is performed between types
- When mixing types, you must use type casting.
- Example:
1 + 2e0
is not allowed because1
is aBIGINT
and2e0
is aDOUBLE
- Example:
1::DOUBLE + 2e0
is allowed because1
is now casted toDOUBLE
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
- When defining features that serve as input parameters using the
External source mappings
- When mapping features to external data sources using
EXTERNAL_xxx()
functions. - Example:
EXTERNAL_SQL(<query> ON <conditions> AS ROW(BIGINT))
- When mapping features to external data sources using
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()
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)
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:
- Standard SQL syntax:
CAST(<feature> AS <new_type>)
- 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.