Skip to content

Commit e08bc05

Browse files
committed
docs: add use-cases section and clarify SQL reference pages
Add seven industry-specific use-case pages (AI/LLM, fintech, healthcare, geospatial, gaming, IoT/edge, e-commerce) covering how each engine combination maps to real product requirements. Expand the time_bucket interval description in the functions reference to cover Postgres-style long-form strings and bare-integer milliseconds. Clarify the multi-vector-column section in the vector search reference: rename "Multi-Vector" to "Multiple vector columns per collection", show the correct column-name syntax for CREATE VECTOR INDEX and ORDER BY, and document the default-field fallback for single-embedding collections.
1 parent 08a051e commit e08bc05

9 files changed

Lines changed: 1167 additions & 5 deletions

docs/sql/functions.rdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ description: Built-in scalar, aggregate, and engine-specific SQL functions.
2424
| `DATE_FORMAT(ts, fmt)` | Format timestamp as string |
2525
| `time_bucket(interval, ts)` | Truncate to interval boundary |
2626

27-
`time_bucket` accepts: `'1s'`, `'5m'`, `'15m'`, `'1h'`, `'6h'`, `'1d'`, `'1w'`, or ISO 8601 (`'PT5M'`, `'P1D'`).
27+
`time_bucket` accepts the interval as a string in any of these forms: short (`'1s'`, `'5m'`, `'15m'`, `'1h'`, `'6h'`, `'1d'`, `'1w'`), long / Postgres-style (`'30 seconds'`, `'5 minutes'`, `'1 hour'`, `'2 hours 30 minutes'`, optionally with an `INTERVAL` prefix), ISO 8601 (`'PT5M'`, `'P1D'`), or a bare integer (milliseconds).
2828

2929
## Type Functions
3030

docs/sql/vector-search.rdx

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -150,11 +150,22 @@ LIMIT 10;
150150

151151
Reciprocal Rank Fusion merges BM25 text results with vector similarity in a single pass.
152152

153-
## Multi-Vector
153+
## Multiple vector columns per collection
154154

155-
Collections can have multiple vector indexes on different embedding columns:
155+
A collection can carry several vector indexes, one per embedding column — name
156+
the column in parentheses after the collection. Each index gets its own metric
157+
and parameters; queries pick the column they search:
156158

157159
```sql
158-
CREATE VECTOR INDEX idx_text ON products METRIC cosine DIM 384;
159-
CREATE VECTOR INDEX idx_image ON products METRIC cosine DIM 512;
160+
CREATE VECTOR INDEX idx_text ON products (text_embedding) METRIC cosine DIM 384;
161+
CREATE VECTOR INDEX idx_image ON products (image_embedding) METRIC cosine DIM 512;
162+
163+
-- Search the text-embedding index:
164+
SELECT id FROM products ORDER BY text_embedding <=> $text_vec LIMIT 10;
165+
-- ...or the image-embedding index:
166+
SELECT id FROM products ORDER BY image_embedding <-> $image_vec LIMIT 10;
160167
```
168+
169+
Omitting `(<column>)` targets the collection's default (unnamed) vector field —
170+
fine when there's only one embedding column (as in the `CREATE VECTOR INDEX
171+
idx_embed ON articles ...` examples above).

use-cases/ai-llm.rdx

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
---
2+
title: AI & LLM Applications
3+
description: Build RAG pipelines, agent memory stores, and hybrid semantic search on NodeDB — vector, graph, full-text, and document engines in one query.
4+
---
5+
6+
Modern AI applications need at least three databases to function: a vector store for embeddings, a graph database for entity relationships, and a search index for keyword recall. NodeDB collapses all three into one, with a fourth engine — schemaless Document — for storing raw LLM outputs and conversation history.
7+
8+
## Engines used
9+
10+
| Engine | Role |
11+
|---|---|
12+
| **Vector** | HNSW similarity search, filtered ANN, multivec MaxSim |
13+
| **Full-Text Search** | BM25 keyword recall, 27-language analyzers |
14+
| **Graph** | Entity relationships, GraphRAG context expansion |
15+
| **Document (schemaless)** | Conversation history, LLM outputs, agent state |
16+
17+
## Hybrid RAG retrieval
18+
19+
A basic RAG retrieval combines vector similarity with BM25 keyword recall, then fuses the two ranked lists with Reciprocal Rank Fusion. `rrf_score(...)` does the fusion inside the planner — one statement, no application-side merge loop.
20+
21+
```sql
22+
-- Hybrid search: vector ANN + BM25, fused with RRF
23+
SELECT
24+
id,
25+
content,
26+
metadata,
27+
rrf_score(
28+
vector_distance(embedding, $query_vec),
29+
bm25_score(content, $query_text)
30+
) AS score
31+
FROM documents
32+
WHERE metadata ->> 'source' = 'internal'
33+
ORDER BY score DESC
34+
LIMIT 10;
35+
```
36+
37+
## GraphRAG: expand context through entity relationships
38+
39+
Entities and their relationships are graph edges layered on the same `documents` collection — graph is an overlay, not a separate store. After vector search finds seed chunks, the fusion DSL walks those edges and merges the rankings:
40+
41+
```sql
42+
-- Wire entities as edges on the documents collection
43+
GRAPH INSERT EDGE IN 'documents' FROM $chunk_id TO $entity_id TYPE 'mentions';
44+
GRAPH INSERT EDGE IN 'documents' FROM $entity_a TO $entity_b TYPE 'related_to' PROPERTIES { weight: 0.8 };
45+
46+
-- Vector search seeds → BFS along 'related_to' → RRF merge of vector rank + hop distance
47+
GRAPH RAG FUSION ON documents
48+
QUERY $query_vec
49+
VECTOR_FIELD 'embedding'
50+
VECTOR_TOP_K 50
51+
EXPANSION_DEPTH 2
52+
EDGE_LABEL 'related_to'
53+
FINAL_TOP_K 10
54+
RRF_K (60.0, 35.0);
55+
```
56+
57+
Need keyword relevance in the same pass? Add the `BM25` leg and the `RRF_K` tuple becomes a triple — vector, graph expansion, and BM25 all fused in one plan:
58+
59+
```sql
60+
GRAPH RAG FUSION ON documents
61+
QUERY $query_vec VECTOR_FIELD 'embedding' VECTOR_TOP_K 50
62+
BM25 $query_text ON 'content'
63+
EXPANSION_DEPTH 2 EDGE_LABEL 'related_to' FINAL_TOP_K 10
64+
RRF_K (60.0, 35.0, 50.0);
65+
```
66+
67+
You can also walk the entity graph directly with a Cypher-subset `MATCH`:
68+
69+
```sql
70+
MATCH (c:Chunk)-[:mentions]->(e:Entity)-[:related_to*1..2]->(related:Entity)
71+
WHERE c.id = $chunk_id
72+
RETURN DISTINCT related.id
73+
LIMIT 20;
74+
```
75+
76+
## Agent memory store
77+
78+
Long-running agents need persistent, queryable memory. A schemaless Document collection stores arbitrary turn payloads; a vector index makes semantic recall fast.
79+
80+
```sql
81+
CREATE COLLECTION agent_memory;
82+
CREATE VECTOR INDEX idx_mem ON agent_memory METRIC cosine DIM 1536;
83+
84+
-- Store a turn (object-literal insert on a schemaless collection)
85+
INSERT INTO agent_memory
86+
{ session_id: $sid, turn: $n, role: $role, content: $text, embedding: $vec, created_at: now() };
87+
88+
-- Recall: recent turns from this session + semantically similar turns from any session
89+
(SELECT content, role, created_at
90+
FROM agent_memory
91+
WHERE session_id = $sid
92+
ORDER BY turn DESC
93+
LIMIT 10)
94+
UNION ALL
95+
(SELECT content, role, created_at
96+
FROM agent_memory
97+
WHERE session_id <> $sid
98+
ORDER BY embedding <=> $query_vec
99+
LIMIT 5)
100+
ORDER BY created_at;
101+
```
102+
103+
## Filtered vector search
104+
105+
Production vector workloads almost always filter by metadata before ranking. NodeDB's NaviX adaptive-local traversal keeps ANN recall high even with tight filters — the planner builds a roaring bitmap of matching IDs and picks pre-filter, post-filter, or brute-force based on selectivity.
106+
107+
```sql
108+
-- Similar products, restricted to in-stock items in the user's region
109+
SELECT id, name, price, embedding <=> $query_vec AS distance
110+
FROM products
111+
WHERE in_stock = true
112+
AND region = $region
113+
AND category_id = ANY($category_ids)
114+
ORDER BY embedding <=> $query_vec
115+
LIMIT 20;
116+
```
117+
118+
<Callout kind="tip" title="Matryoshka adaptive dimensions">
119+
Store full-precision embeddings and rank on the first N dimensions for a fast first pass via the `query_dim` tuning argument — then re-rank the top candidates at full precision. No reindexing:
120+
121+
```sql
122+
SELECT id, vector_distance(embedding, $query_vec, query_dim => 256) AS distance
123+
FROM products
124+
WHERE in_stock = true
125+
ORDER BY distance
126+
LIMIT 100;
127+
```
128+
</Callout>
129+
130+
## Why not a dedicated vector database?
131+
132+
Dedicated vector databases force you to maintain a separate store for every non-vector data shape. When your RAG pipeline needs keyword fallback, entity relationships, and conversation history, you end up with four systems, four consistency boundaries, and cross-system joins that travel over the network.
133+
134+
NodeDB keeps everything in one process with a single shared snapshot. A hybrid search + graph expansion + memory recall query is one SQL statement with zero inter-process calls.

use-cases/ecommerce-search.rdx

Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
---
2+
title: E-commerce & Search
3+
description: Hybrid BM25 + vector product search, RRF fusion ranking, recommendation graphs, and real-time inventory analytics — without Elasticsearch, a vector database, or a separate OLAP cluster.
4+
---
5+
6+
E-commerce search stacks are notoriously complex: Elasticsearch for keyword search, a vector database for semantic similarity, a recommendation engine backed by a graph database, and a data warehouse for sales analytics. NodeDB provides all four as peer engines (plus Graph and FTS overlays) sharing one storage core, one query planner, and one operational surface.
7+
8+
## Engines used
9+
10+
| Engine | Role |
11+
|---|---|
12+
| **Full-Text Search** | BM25 keyword search, faceted filtering, autocomplete |
13+
| **Vector** | Semantic product search, visual similarity, embedding-based recommendations |
14+
| **Graph** | "Customers also bought", category navigation, brand-product relationships |
15+
| **Document (schemaless)** | Product catalog, flexible attributes, variant metadata |
16+
| **Columnar** | Sales analytics, inventory reporting, cohort analysis |
17+
18+
## Hybrid product search
19+
20+
The strongest e-commerce search combines keyword precision with semantic recall, then fuses the ranked lists. `rrf_score(...)` does the fusion in the planner — no application-side merge, no Elasticsearch + Pinecone glue code.
21+
22+
```sql
23+
CREATE COLLECTION products;
24+
CREATE SEARCH INDEX ON products FIELDS title, description ANALYZER 'english' FUZZY true;
25+
-- One vector index per embedding column on the same collection.
26+
CREATE VECTOR INDEX idx_text ON products (text_embedding) METRIC cosine DIM 384;
27+
CREATE VECTOR INDEX idx_image ON products (image_embedding) METRIC cosine DIM 512;
28+
29+
-- Hybrid search: BM25 keyword + vector semantic, fused with RRF
30+
SELECT
31+
id,
32+
title,
33+
price,
34+
brand,
35+
rrf_score(
36+
vector_distance(text_embedding, $query_vec),
37+
bm25_score(description, $query_text)
38+
) AS relevance
39+
FROM products
40+
WHERE in_stock = true
41+
AND price BETWEEN $min_price AND $max_price
42+
AND category_id = ANY($category_ids)
43+
ORDER BY relevance DESC
44+
LIMIT 20;
45+
```
46+
47+
## Autocomplete and faceted filtering
48+
49+
```sql
50+
-- Autocomplete: fuzzy title match, ranked by popularity
51+
SELECT DISTINCT id, title
52+
FROM products
53+
WHERE text_match(title, $prefix, { fuzzy: true, distance: 1 })
54+
ORDER BY popularity_score DESC
55+
LIMIT 8;
56+
57+
-- Facet counts: products per brand in the current keyword result set
58+
SELECT brand, count(*) AS count
59+
FROM products
60+
WHERE text_match(description, $query_text)
61+
AND in_stock = true
62+
GROUP BY brand
63+
ORDER BY count DESC
64+
LIMIT 15;
65+
```
66+
67+
## Visual similarity search
68+
69+
For fashion, furniture, and home goods, image-embedding similarity lets shoppers find products that look alike — searching the `image_embedding` index created above, right alongside the catalog row:
70+
71+
```sql
72+
-- "Shop similar items" — products whose image embedding is closest to the
73+
-- viewed one, restricted to the same in-stock category.
74+
SELECT id, title, price, image_url,
75+
image_embedding <=> $viewed_vec AS visual_distance
76+
FROM products
77+
WHERE category_id = $category_id
78+
AND in_stock = true
79+
AND id <> $viewed_product_id
80+
ORDER BY image_embedding <=> $viewed_vec
81+
LIMIT 12;
82+
```
83+
84+
## Recommendation graph — "Customers also bought"
85+
86+
Co-purchase relationships are graph edges layered on the `products` collection — graph is an overlay, you don't create a separate "graph collection." Traversal-based recommendations run alongside the catalog.
87+
88+
```sql
89+
-- A batch job recomputes co-purchase counts and (re)writes the edge with the
90+
-- current weight — DELETE then INSERT, since GRAPH INSERT EDGE is insert-only.
91+
GRAPH DELETE EDGE IN 'products' FROM $product_a TO $product_b TYPE 'co_purchased';
92+
GRAPH INSERT EDGE IN 'products' FROM $product_a TO $product_b TYPE 'co_purchased'
93+
PROPERTIES { weight: $copurchase_count };
94+
95+
-- "Customers also bought": direct co-purchase neighbours of a product,
96+
-- ranked by co-purchase weight. Graph nodes are document rows, so their
97+
-- catalog fields are available in RETURN.
98+
MATCH (seed:Product)-[e:co_purchased]->(rec:Product)
99+
WHERE seed.id = $product_id
100+
AND rec.in_stock = true
101+
RETURN rec.id, rec.title, rec.price, e.weight AS affinity
102+
ORDER BY e.weight DESC
103+
LIMIT 8;
104+
```
105+
106+
## Real-time inventory — LIVE SELECT
107+
108+
Push in-stock / out-of-stock transitions to product pages without polling. `LIVE SELECT` registers a query and streams matching changes over pgwire, WebSocket, or the native protocol.
109+
110+
```sql
111+
-- A shopper viewing their wishlist subscribes to back-in-stock events
112+
LIVE SELECT id, title FROM products
113+
WHERE id = ANY($wishlist_ids)
114+
AND in_stock = true;
115+
116+
-- When a back-ordered item is restocked, every subscriber gets the row
117+
-- delivered immediately. Cancel with: CANCEL LIVE SELECT <subscription_id>;
118+
```
119+
120+
## Sales analytics — Columnar engine
121+
122+
The Columnar engine handles aggregation-heavy analytics across millions of orders without a separate data warehouse — per-column compression, block statistics, and predicate pushdown.
123+
124+
```sql
125+
CREATE COLLECTION orders (
126+
id TEXT PRIMARY KEY,
127+
placed_at TIMESTAMP TIME_KEY,
128+
status TEXT
129+
) WITH (engine='columnar');
130+
131+
-- Revenue by category, last 30 days
132+
SELECT
133+
c.name AS category,
134+
count(DISTINCT o.id) AS orders,
135+
sum(oi.quantity * oi.price) AS revenue,
136+
avg(oi.quantity * oi.price) AS avg_order_value
137+
FROM orders o
138+
JOIN order_items oi ON oi.order_id = o.id
139+
JOIN products p ON p.id = oi.product_id
140+
JOIN categories c ON c.id = p.category_id
141+
WHERE o.placed_at >= now() - INTERVAL '30 days'
142+
AND o.status = 'completed'
143+
GROUP BY c.name
144+
ORDER BY revenue DESC;
145+
146+
-- Conversion funnel: views → add-to-cart → purchase
147+
SELECT
148+
time_bucket('1d', e.ts) AS day,
149+
count(CASE WHEN e.event = 'product_view' THEN 1 END) AS views,
150+
count(CASE WHEN e.event = 'add_to_cart' THEN 1 END) AS add_to_cart,
151+
count(CASE WHEN e.event = 'purchase' THEN 1 END) AS purchases,
152+
round(
153+
count(CASE WHEN e.event = 'purchase' THEN 1 END)::numeric
154+
/ nullif(count(CASE WHEN e.event = 'product_view' THEN 1 END), 0) * 100,
155+
2
156+
) AS conversion_pct
157+
FROM events e
158+
WHERE e.ts >= now() - INTERVAL '14 days'
159+
GROUP BY day
160+
ORDER BY day;
161+
```
162+
163+
<Callout kind="tip" title="Matryoshka embeddings for tiered search">
164+
Store full-precision embeddings and rank the first N dimensions for a fast first pass via the `query_dim` tuning argument, then re-rank the top candidates at full precision — no reindexing:
165+
166+
```sql
167+
SELECT id, title, vector_distance(text_embedding, $query_vec, query_dim => 256) AS distance
168+
FROM products
169+
WHERE in_stock = true
170+
ORDER BY distance
171+
LIMIT 100;
172+
```
173+
</Callout>
174+
175+
## Why not Elasticsearch + a vector database?
176+
177+
Elasticsearch handles keyword search but requires a separate vector database for semantic recall, a graph database for recommendations, and a data warehouse for analytics. Synchronising product-catalog changes across four systems is the dominant operational burden. NodeDB keeps every engine in the same process: a hybrid search + recommendation + inventory check is one SQL statement, not four API calls.

0 commit comments

Comments
 (0)