LLM bootstrap — orientation and self-learning
This page is the entry point for an LLM that needs to write or reason about FeatureQL. Read this page first, then load additional pages on demand based on the task.
What FeatureQL is
FeatureQL is a functional, entity-centric DSL that transpiles to SQL dialects (DuckDB, Trino, BigQuery, DataFusion). You write flat lists of named expressions called features. Features are pure functions — they compose through automatic dependency tracking, with no CTEs, no subqueries, and no imperative control flow.
A minimal working query:
WITH
ID := INPUT(BIGINT),
SELECT
ID,
DOUBLED := ID * 2
FOR
ID := BIND_VALUES(ARRAY[1, 2, 3]);
-- Result: (1, 2), (2, 4), (3, 6) Every query must have a BIND_XXX() in the FOR clause or it will not execute. This is the single most common mistake.
Page index — load what you need
| Page | Load when... |
|---|---|
| Syntax reference | You need to write or debug FeatureQL syntax: types, casting, operators, query structure |
| Data sources | You need to connect features to tables, inline data, or bind input values |
| Modeling | You need to model entities, traverse relationships with EXTEND/TRANSFORM/RELATED |
| Recipes | You need patterns for common tasks: aggregation, filtering, analytics, feature engineering |
Self-learning: query the system dynamically
You do not need all knowledge in your context window. FeatureQL has built-in discovery commands you can execute at any time:
-- Browse all documentation pages (titles only, fast)
SHOW DOCS (EXCLUDE (CONTENT)) WHERE CATEGORY='DOC_PAGE' ORDER BY NAME;
-- Search documentation content for a topic
SHOW DOCS WHERE CONTENT LIKE '%EXTEND%' AND CATEGORY = 'CODE_SAMPLE';
-- Get function signatures (exact syntax, parameters, return types)
SHOW FUNCTION SIGNATURES WHERE NAME IN ('EXTEND', 'TRANSFORM', 'EXTERNAL_COLUMNS');
-- Get executable test cases for a function (learn by example)
SHOW FUNCTION TESTS WHERE FUNCTION_NAME = 'EXTEND';
-- Explore persisted features in a namespace
SHOW FEATURES WHERE FUNCTION IN ('ENTITY', 'INPUT', 'EXTERNAL_COLUMNS');
SHOW FEATURES WHERE NAME LIKE 'finance.%';
SHOW FEATURES WHERE IS_DOWNSTREAM_OF(CUSTOMER_ID); Use these aggressively. When you encounter an unfamiliar function, run SHOW FUNCTION SIGNATURES before guessing. When you need a pattern, search SHOW DOCS or SHOW FUNCTION TESTS before inventing one.
Efficient exploration workflow
When starting a new task on an existing codebase:
- Discover what exists —
SHOW FEATURES WHERE FUNCTION IN ('ENTITY', 'INPUT', 'EXTERNAL_COLUMNS'); - Find relevant features —
SHOW FEATURES WHERE NAME LIKE 'finance.%';orSHOW FEATURES WHERE IS_DOWNSTREAM_OF(CUSTOMER_ID); - Verify function syntax —
SHOW FUNCTION SIGNATURES WHERE NAME = 'function_name'; - Find examples —
SHOW FUNCTION TESTS WHERE FUNCTION_NAME = 'function_name';
Error recovery
When a query fails, read the error message carefully — it includes the location in the query. Then check this list:
| Symptom | Likely cause | Fix |
|---|---|---|
| "Missing binding" or query won't execute | No BIND_XXX() in FOR clause | Add FOR INPUT_NAME := BIND_VALUES(ARRAY[...]) |
| Type mismatch | No automatic coercion in FeatureQL | Cast explicitly: value::TYPE or CAST(value AS TYPE) |
| Unknown function | Typo or wrong name | Verify: SHOW FUNCTION SIGNATURES WHERE NAME = 'name' |
| Entity mismatch | Annotations don't match (BIGINT#CUSTOMERS vs BIGINT#ORDERS) | Check entity annotations on INPUT and foreign keys |
| "Cannot resolve feature" | Feature not in scope | Check namespace: FROM fm.namespace or use fully qualified name |
General debugging strategy: remove features one by one until the query works, then add them back to isolate the problem.
Critical differences from SQL
These will trip you up if you think in SQL:
- No CTEs, no subqueries. A query is a flat list of
feature := expressiondefinitions. Features can reference each other in any order — dependency resolution is automatic. - Mandatory binding. Every query needs
BIND_VALUE(),BIND_VALUES(),BIND_SQL(), orBIND_COLUMNS()in theFORclause if one dependency of the returned feature is anINPUT()feature. - Limited type coercion.
1 + 1.0is an error (integer + decimal). DECIMAL scalar arithmetic auto-coerces between precisions (1.1 + 1.25works), but array and ROW constructors do not (ARRAY[ROW(1.1 AS v), ROW(1.25 AS v)]fails). Use DOUBLE withe0suffix to avoid surprises:1.25e0. - Entity-centric. Features are organized around entities (CUSTOMER, ORDER, PRODUCT), not tables. The mapping to tables is done via
EXTERNAL_COLUMNS(). - GROUP BY is per-feature. Write
SUM(amount) GROUP BY category, not a separateGROUP BYclause at the end. - Only two scopes: features and fields. Top-level features reference other features. Inside
TRANSFORM(), you work with fields from the array. There is never a third nesting level. This keeps reasoning simple:EXTEND()brings cross-entity data into the array,TRANSFORM()processes it. Think of it as a pipeline, not nested subqueries. - Always write explicit
VIA ... BIND TOin EXTEND. Auto-inference exists but explicit binding is clearer and avoids ambiguity errors.