dbt Adapter (dbt-benostreamdb)¶
The dbt-benostreamdb adapter enables analytics engineering, automated data transformations, and data lineage directly over BenoStreamDB and Apache Iceberg tables.
Overview¶
With dbt-benostreamdb, you can define models using familiar dbt SQL workflows while taking full advantage of BenoStreamDB’s native capabilities:
Iceberg Table & View Materializations: Build standard Parquet tables and metadata views without moving data out of your lakehouse.
Incremental Materializations: Efficient append and merge models leveraging primary keys and deletion vectors.
Vector & Hybrid Search Macros: Embedded macros for vector distance scoring and hybrid retrieval (
benostreamdb.vector_search(...)).Graph Traversal Macros: Native relationship walks (
benostreamdb.graph_neighbors(...),benostreamdb.graph_shortest_path(...)) over edge tables.
Installation¶
Install the adapter via pip:
pip install dbt-benostreamdb
Or install from source within the repository:
cd dbt-benostreamdb
pip install -e .
Connection Profile (profiles.yml)¶
Configure your target connection in ~/.dbt/profiles.yml:
benostreamdb_profile:
target: dev
outputs:
dev:
type: benostreamdb
database: default
schema: public
threads: 4
uri: "file:///path/to/benostreamdb/warehouse"
For cloud object storage targets (S3 / GCS / Azure), specify the storage URI:
benostreamdb_profile:
target: prod
outputs:
prod:
type: benostreamdb
database: default
schema: analytics
threads: 8
uri: "s3://my-lakehouse-bucket/warehouse"
Remote runs: dbt-trino (no warehouse mount)¶
dbt-benostreamdb is an embedded adapter — it opens the warehouse in-process,
so the path/URI must be reachable from wherever dbt runs. For Airflow, Cloud
Run, or CI, point dbt-trino at a Trino coordinator that has the
trino-benostreamdb plugin installed:
benostreamdb_remote:
target: prod
outputs:
prod:
type: trino
method: none # or ldap / jwt, per your coordinator
host: trino.internal
port: 8080
catalog: benostream # must match connector.name=benostream in etc/catalog
schema: default
threads: 8
This is the supported remote path: the connector already pushes
system.vector_search, system.hybrid_search (including its filter
argument), system.list_indexes, and system.regional_drift_search down to the
engine, so remote dbt models keep the same search semantics as embedded runs.
Keep the embedded adapter for local development and for Python-API features such
as index building.
Which one to use¶
|
|
|
|---|---|---|
Infrastructure |
none — |
Trino coordinator + JVM + plugin + catalog file |
Where the warehouse must live |
reachable from the dbt process (mounted path / object store creds) |
coordinator only |
Concurrency / auth / isolation |
single process |
Trino’s (users, resource groups, queues) |
Search functions |
Python API and SQL macros |
|
Python-API features (index builds, embeddings, graph helpers) |
yes |
no |
Best for |
laptops, CI, single-node lakehouse, notebooks |
Airflow, Cloud Run, multi-team clusters |
The embedded adapter exists precisely so that “run dbt against BenoStreamDB” does not require standing up a Trino cluster — that is the dependency it removes, and it is why both paths are kept: the engine is the same, only the transport differs. Keep the adapter thin (a SQL adapter over the engine’s session) and do not grow transport-only features in it; anything that needs a cluster belongs on the Trino path.
Type mapping¶
datetime columns map to timestamp (previously this raised
NotImplementedException), next to the existing
text → varchar, number → double, boolean → boolean, date → date, and
time → time mappings. Trino exposes the same columns as TIMESTAMP, so a model
built embedded also runs unchanged through dbt-trino.
Supported Materializations¶
1. Table Materialization¶
Creates an authoritative Apache Iceberg table:
{{ config(
materialized='table',
format_version=2
) }}
SELECT
id,
title,
category,
embedding
FROM {{ ref('stg_documents') }}
WHERE is_active = true
2. Incremental Materialization¶
Processes only new or updated rows since the last dbt execution:
{{ config(
materialized='incremental',
unique_key='id'
) }}
SELECT
id,
user_id,
event_name,
event_timestamp
FROM {{ ref('stg_events') }}
{% if is_incremental() %}
WHERE event_timestamp > (SELECT max(event_timestamp) FROM {{ this }})
{% endif %}
Vector & Search Macros¶
BenoStreamDB exposes helper macros for similarity queries and vector analytics:
{{ config(materialized='table') }}
SELECT
id,
title,
embedding <-> '[0.12, 0.45, -0.23]'::vector AS l2_dist,
embedding <=> '[0.12, 0.45, -0.23]'::vector AS cosine_dist
FROM {{ ref('catalog_items') }}
ORDER BY cosine_dist ASC
LIMIT 50
Graph Traversal Macros¶
Query relationship models and edge tables directly in your dbt models:
{{ config(materialized='table') }}
-- Traverse 2 hops from seed entity 101
SELECT *
FROM graph_neighbors('{{ ref("stg_edges") }}', '101', 2, 'auto')
Table-Level Cluster Indexing & Warmup Macros¶
For high-scale vector models (multi-million rows), you can register post-hooks in your model configuration to trigger global Voronoi cluster index builds and cache warming immediately following table materializations:
{{ config(
materialized='table',
post_hook=[
rebuild_cluster_index(this, column='embedding', k=1024, m=16, ef_construction=200, target_shard_mb=512),
warmup(this, n_probes=500, max_memory_gb=8.0)
]
) }}
SELECT
id,
title,
embedding
FROM {{ ref('raw_embeddings') }}
Macros available in macros/ddl.sql:
{{ rebuild_cluster_index(relation, column='embedding', k=1024, m=16, ef_construction=200, target_shard_mb=512) }}: EmitsALTER TABLE <relation> EXECUTE rebuild_cluster_index(...), consolidating multi-segment vectors into rolling $\le 512\text{ MiB}$ Puffin index shards sealed into the Iceberg manifest.{{ warmup(relation, n_probes=100, max_memory_gb=8.0) }}: EmitsALTER TABLE <relation> EXECUTE warmup(...), probing the Voronoi centroid router to prefault index shards into the OS page cache before live queries arrive.
Testing & Verification¶
Run standard dbt commands to validate and execute models:
# Test connection
dbt debug
# Run models
dbt run
# Run tests
dbt test