← 1.0.0 scope SAPE-37

Exact nearest-neighbour search, declared like every other read

Status
In progress
Component
server
Priority
High — the owner put a RAG-capable vector store into 1.0.0
Blocked by
Blocks
SAPE-39; SAPE-38 needs its measurement

Why it is here

The owner decided that 1.0.0 is a vector database that can serve RAG — retrieval-augmented generation, where an application finds the passages most similar to a question and hands them to a language model. Measured before any of this was written: the engine had no vector code at all. A document body is unconstrained JSON, so a vector could already be stored as an array of numbers. Nothing could search by one.

sapedb does not produce embeddings. The application calls an embedding model and stores the vector it gets back; the database stores and searches. That is the same division every vector store draws, and it is said here so nobody expects otherwise.

What RAG needs from it, and how each maps onto this store

Why exact comes first, and alone

Exact search compares every row in the range it walks and returns the true top-k. Its cost is declared — rows walked times dimension — and its answer is correct. That is this product's argument, kept intact the same way hashRange keeps it.

Approximate search (HNSW, IVF) is what a large RAG corpus eventually needs, and it breaks a different promise: not cost but correctness, and silently. That is SAPE-38. How far exact search stretches before approximate search becomes necessary is being measured now, at the dimensions RAG actually uses.

What it is

The one open question inside it

What happens at the row ceiling. hashRange refuses, because a digest of part of a range is indistinguishable from a digest of all of it. deleteRange returns a cursor, because its answer says it stopped. The k nearest of a prefix are not the k nearest of the range, so an answer that stopped early without saying so would be silently wrong. Which precedent applies is being worked out with the implementation, and the reasoning will be on this page.

Out of scope

Acceptance criteria

  1. The answer equals a brute-force top-k computed outside the package from the same vectors — a guard that computes its expectation through the same code is circular.
  2. Each metric is checked on vectors where the metrics disagree about the order, so a swapped metric fails.
  3. The result carries the row's projected fields, so a caller gets the chunk text back in one call.
  4. A search bounded to one document's key prefix returns only that document's chunks.
  5. A wrong-dimension, missing or non-numeric row refuses and names the key; a wrong-dimension query refuses before any row is read.
  6. Ties are deterministic, pinned with vectors built to tie.
  7. The row ceiling is counted per run, proven across partitions.
  8. Memory scales with k, not with the range — measured.
  9. Every new guard watched failing.