The movie demo corpus

just stack already serves the default movies index over a small slice of Wikipedia movie plots — enough to start exploring semantic, lexical, and hybrid search. This page’s opt-in just demo-data step upgrades that index to the full movie corpus: a larger, decade-balanced set of recognizable films (1980s–2010s, CC-BY-SA-4.0) loaded into the lakehouse as growlerdb.movies. At that scale the three search modes visibly differ, the facets (genre, origin, decade) mean something, and an MCP-connected agent has real substance to answer from.

With the stack up (just stack):

just demo-data

This downloads the pre-sliced parquet (a GitHub release asset) and writes it into Iceberg first (growlerdb.movies, the system of record, same lakehouse-first shape as everything else). The movies index is served by the HA placement pool (pool-a/pool-b): the control plane places its primary on one of the pool nodes, and that node builds the index from growlerdb.movies on assignment, embedding each plot locally (bge-small-en-v1.5 on ONNX Runtime, no API key, no egress). just demo-data recreates the pool nodes so the index is rebuilt from the freshly loaded corpus. The gateway routes {"index":"movies"} requests to the pool automatically, and the demo token is already scoped to the index. To keep embedding fast, the vector is built from a short synopsis (the first few plot sentences); the full plot is kept for lexical search and reading.

The default is 5000 films. The local ONNX embedder does ~500 docs/s, so build, embed, and serve takes about 45 seconds. Watch the build, or change the size:

docker compose -f deploy/compose/docker-compose.yml --profile pool logs -f pool-a pool-b
DEMO_DATA_SIZE=0 just demo-data      # the full corpus

(Air-gapped or re-slicing? Drop a parquet into deploy/compose/demo-data/local/ and set DEMO_DATA_FILE=/local/<name>.parquet; regenerate a slice with deploy/compose/demo-data/build_movies_slice.py. Re-running just demo-data is idempotent; the table converges to the parquet’s rows.)

Where hybrid earns its keep

Try the three modes on the same information need, finding films where a machine turns on its makers:

TOKEN=$(curl -s localhost:8081/v1/login -H 'content-type: application/json' \
  -d '{"username":"demo","password":"demo-growlerdb"}' | jq -r .token)

# Lexical: exact terms only, misses films that never spell it out this way.
curl -s localhost:8081/v1/search -H 'content-type: application/json' \
  -H "authorization: Bearer $TOKEN" \
  -d '{"index":"movies","query":"plot:\"artificial intelligence\"","limit":5}' | jq '.hits[].fields.title'

# Semantic: meaning, not words; finds them however the plot phrases it.
curl -s localhost:8081/v1/search:semantic -H 'content-type: application/json' \
  -H "authorization: Bearer $TOKEN" \
  -d '{"index":"movies","vector_field":"plot_vec","query_text":"a machine becomes self-aware and turns against its creators","k":5}' | jq '.hits[].fields.title'

# Hybrid: RRF-fuses both, a film strong lexically AND semantically ranks above either alone.
curl -s localhost:8081/v1/search:hybrid -H 'content-type: application/json' \
  -H "authorization: Bearer $TOKEN" \
  -d '{"index":"movies","vector_field":"plot_vec","query_text":"a machine becomes self-aware and turns against its creators","k":5,"hydrate":true}' | jq '.hits[] | {title: .fields.title, row: .row.title}'

The hybrid call also demonstrates inline hydration ("hydrate": true): each fused hit carries its authoritative row from Iceberg in the same response. Facet the corpus by field, whether genre, origin, or decade:

curl -s localhost:8081/v1/facets -H 'content-type: application/json' \
  -H "authorization: Bearer $TOKEN" \
  -d '{"index":"movies","query":"*:*","fields":["genre"],"size":10}' | jq

Ask an agent

This corpus is what makes the MCP hookup (getting-started §7) compelling: connect Claude (or any MCP client) and ask “what films in the corpus deal with AI or robots turning on people?” The agent runs hybrid retrieval over the movie plots and answers grounded in governed rows with citations, scoped to what the demo token may see. As always, GrowlerDB never calls an LLM; retrieval with citations is the product, and the model stays yours.


GrowlerDB — AGPL-3.0. Search returns coordinates; rows hydrate from Iceberg.

This site uses Just the Docs, a documentation theme for Jekyll.