Your browser sucks, JavaScript disabled! Gunnar's console needs JavaScript and WebAssembly. Git over SSH on port 2222 does not.

nordisk/skade

.nornir/warehouse-capability-gaps.md

Warehouse capability-gap audit — "is the warehouse enough?"

Rickard: "is the warehouse enough, falkordb, more data structures?"

Scope. This audits what the nordisk warehouse can answer today across three repos — skade/skade-katalog (the embedded Iceberg data plane), the nornir warehouse tables, and korp's FalkorDB graph tab — and lists every data-structure / index gap, evaluating each: needed? reuse-first (extend skade / consume an existing engine) vs. new? It is deliberately long and opinionated so the eternal loop has a ranked backlog to draw the next round's work from.

Round-1 verdict up front: the warehouse is a strong OLAP core (columnar + ACID + time-travel) but is a one-trick access-path — everything is a min/max-skipped, bloom-probed linear/columnar scan. It has no secondary index families beyond bloom filters. The single highest-value missing structure (a spatial index) is implemented this round; the rest are ranked below.


1. What exists today (the baseline)

1a. skade-katalog — the catalog plane

1b. skade — the data plane

1c. nornir warehouse tables

1d. korp — the FalkorDB graph tab

Access-path matrix (today):

Query shape Structure serving it Status
Full scan / analytical SQL Parquet + DataFusion ✅ strong
Column point lookup bloom filter + min/max skip ✅
Range scan on sorted col min/max file skip + SortOrder ✅
Snapshot / time-travel (by id) Ragnar STree64 ✅
Time-travel by timestamp commit-log ts_micros reverse scan ✅ shipped this round (load_table_as_of)
Partition prune identity partitions ✅ (identity only)
Graph traversal (Cypher) FalkorDB (korp UI only) ⚠️ not a warehouse backend
Spatial (bbox / radius / kNN) — ✅ shipped this round (skade::spatial)
Time-series (downsample / gap-fill / ASOF join) — ❌ gap
Full-text search — ❌ gap
Vector / ANN (RAG) — ❌ gap
Bitmap / low-cardinality set ops — ❌ gap
Materialized views — ❌ gap
Secondary / covering index on arbitrary col bloom only (probabilistic) ⚠️ partial

2. Is columnar + ACID + time-travel enough?

For the OLAP core: yes. For a warehouse that also wants to serve OSM/geoint, RAG, observability/timeseries, lineage-graph queries, and text search — no. Those are different access-path problems, and Iceberg's design answer is "pluggable table formats + secondary indexes," which skade has only begun (bloom). The value is not in replacing the columnar core but in adding first-class secondary structures behind one warehouse trait so a consumer picks the access path per query, over the same ACID/time-travelled data.

The rest of this doc is the ranked gap list. Each entry: need, reuse-first verdict, shape of the work.


3. GAP: Spatial index (R-tree / geohash / Z-order) — ★ HIGHEST VALUE — DONE this round


4. GAP: First-class GRAPH store (FalkorDB / Cypher) as a warehouse backend — ★ 2nd


5. GAP: Time-series structures (downsample / gap-fill / ASOF join / retention)


6. GAP: Full-text SEARCH index (inverted index / FST / BM25)


7. GAP: Vector index for RAG / embeddings (HNSW / IVF, ANN)


8. GAP: Bitmap indexes (roaring) for low-cardinality columns & set ops


9. GAP: Materialized views / rollups (incremental maintenance)


10. GAP: Richer secondary / covering indexes (exact, not just bloom)


11. Cross-cutting: the missing abstraction, not just the structures

The real gap under all of §3–§10 is that skade has no pluggable SecondaryIndex / warehouse-backend trait that lets an index family (spatial, graph, FTS, vector, bitmap) share: build from batches, persist as an Iceberg side-table (so it time-travels with the data), prune in read_filtered, and register on the warehouse seam (korp's Warehouse/Streaming traits are the seed — promote them into skade/a shared crate). This round ships the first concrete member (spatial) with the from_batches shape deliberately chosen to become that template. The recommended arc:

  1. spatial (done) — establishes the from_batches build seam. ← this round
  2. graph backend trait — promote korp's seam, wrap FalkorExecutor + embedded redb-graph fallback.
  3. time-series — Iceberg time-transforms + DataFusion ASOF/downsample UDFs.
  4. bitmap (roaring) — cheapest broad win; wires the facet UIs.
  5. full-text (tantivy/fst) — reuses the build seam.
  6. materialized views — off the existing CDC stream + cursor.
  7. vector/ANN (hnsw) — only when a RAG consumer is real.
  8. SecondaryIndex trait + side-table registry — factor the shared plumbing out of 1–7; add ScanFilter::Bbox / exact secondary pruning to read_filtered.

Answer to the question: the columnar+ACID+time-travel core is enough as a core. FalkorDB should be promoted from a korp-UI sidecar to a warehouse backend behind a trait (not left where it is). And yes — the warehouse needs more data structures: spatial (shipped), then graph, time-series, bitmap, full-text, matviews, and (later) vector — all as pluggable, time-travelling side-table indexes behind one seam, reusing existing crates (FalkorDB, tantivy, roaring, hnsw) rather than hand-rolled twins.