Cookbook / Search and retrieval · Intermediate
Query the relate docs
The relate documentation is published as a relate graph: pages and their sections, the links and prerequisites between pages, the glossary terms each page uses, the surfaces it applies to, and a catalog of capabilities that cite the pages. This recipe loads that structure, with every section of five pages and their vectors, and asks two questions. Full-text search misses the first one; vector search finds the sections that answer it, and the graph turns them into context: the pages to read first and the terms to know. For the second, the nearest section is about a different kind of "Cursor"; a traversal chooses the pages to search, which sets it aside, and two more hops reach the capability the answer belongs to and the page or recipe that proves it.
The graph is the docs graph's own shape, cut down. A (:Page {id, title, url}) has HAS_SECTION relationships to its (:Section {id, page, heading, position, url, text, embedding}) nodes, LINKS_TO and PREREQUISITE_OF
relationships to other pages, USES_TERM to a (:Term {id, term, short})
and APPLIES_TO to a (:Surface {id}). A (:Capability {id, title, url})
CITES pages and SOLVES a (:Problem {id, kind, text}); a
(:CookbookRecipe {id, title, url}) DEMONSTRATES capabilities, and a
capability is VERIFIED_BY a recipe or a page. Each question is a
(:Question {text, embedding}) node, so a step can search with a stored
vector.
Where the data comes from
The docs build writes the whole graph as a Cypher script,
https://relatedb.ai/docs/graph.cypher. Every section there carries a vector
from bge-small-en-v1.5, a
small open embedding model: 384 floats computed from the page title, the
heading and the section text, with CLS pooling, normalized. This recipe keeps
the graph's pages, terms, surfaces, capabilities, problems and recipes, and
the relationships among them that the steps read. It keeps the sections of
five pages only: two agent guides, the vector search guide, the search
reference, and the Python API reference, whose Cursor class is a database
cursor. Each section's text is cut to 480 characters and each vector rounded
to four decimals, so a score here can differ from the same search on the
whole graph in the fourth decimal. The two questions were embedded the same
way.
The vectors were computed ahead of time with transformers.js
(@huggingface/transformers 4.3.1), which runs the model's ONNX weights in
Node.js:
import { pipeline } from "@huggingface/transformers";
const embed = await pipeline("feature-extraction", "Xenova/bge-small-en-v1.5",
{ dtype: "fp32", revision: "ea104dacec62c0de699686887e3f920caeb4f3e3" });
const question = "stop the model from deleting nodes";
const out = await embed([question], { pooling: "cls", normalize: true });
const v = out.tolist()[0]; // 384 floats: pass it as the vector to search withCreate the constraints and search indexes
The index names are the docs graph's: docs, a full-text index over each
section's heading and text, and docs_vector, a vector index over the
section embeddings (384 dimensions, cosine). WITH [s.page] declares the
one property a vector search may filter on before it ranks. A statement
written here runs unchanged on the whole graph.
Show the setup step
CREATE CONSTRAINT page_id IF NOT EXISTS FOR (p:Page) REQUIRE p.id IS UNIQUE;
CREATE CONSTRAINT section_id IF NOT EXISTS FOR (s:Section) REQUIRE s.id IS UNIQUE;
CREATE CONSTRAINT term_id IF NOT EXISTS FOR (t:Term) REQUIRE t.id IS UNIQUE;
CREATE CONSTRAINT surface_id IF NOT EXISTS FOR (s:Surface) REQUIRE s.id IS UNIQUE;
CREATE CONSTRAINT capability_id IF NOT EXISTS
FOR (c:Capability) REQUIRE c.id IS UNIQUE;
CREATE CONSTRAINT cookbookrecipe_id IF NOT EXISTS
FOR (r:CookbookRecipe) REQUIRE r.id IS UNIQUE;
CREATE CONSTRAINT question_text IF NOT EXISTS
FOR (q:Question) REQUIRE q.text IS UNIQUE;
CREATE FULLTEXT INDEX docs IF NOT EXISTS FOR (s:Section) ON EACH [s.heading, s.text];
CREATE VECTOR INDEX docs_vector IF NOT EXISTS
FOR (s:Section) ON (s.embedding) WITH [s.page]
OPTIONS {indexConfig: {`vector.dimensions`: 384,
`vector.similarity_function`: 'cosine'}}(no rows) · indexes added: 2, constraints added: 7
Load the graph
Each page row lists the ids its relationships point at, and each statement of
the load turns one list into relationships. vector(r.e, 384, FLOAT32) stores a
typed vector, and fails the statement if an embedding has the wrong length.
Show the setup step
UNWIND $pages AS r
CREATE (:Page {id: r.id, title: r.title, url: r.url});
UNWIND $terms AS r
CREATE (:Term {id: r.id, term: r.term, short: r.short});
UNWIND $pages AS r
MATCH (a:Page {id: r.id})
UNWIND r.links_to AS id
MATCH (b:Page {id: id})
CREATE (a)-[:LINKS_TO]->(b);
UNWIND $pages AS r
MATCH (a:Page {id: r.id})
UNWIND r.prerequisite_of AS id
MATCH (b:Page {id: id})
CREATE (a)-[:PREREQUISITE_OF]->(b);
UNWIND $pages AS r
MATCH (p:Page {id: r.id})
UNWIND r.uses_term AS id
MATCH (t:Term {id: id})
CREATE (p)-[:USES_TERM]->(t);
UNWIND $pages AS r
MATCH (p:Page {id: r.id})
UNWIND r.applies_to AS id
MERGE (s:Surface {id: id})
CREATE (p)-[:APPLIES_TO]->(s);
UNWIND $capabilities AS r
CREATE (c:Capability {id: r.id, title: r.title, url: r.url})
WITH c, r
UNWIND r.solves AS x
CREATE (c)-[:SOLVES]->(:Problem {id: x.id, kind: x.kind, text: x.text});
UNWIND $capabilities AS r
MATCH (c:Capability {id: r.id})
UNWIND r.cites AS id
MATCH (p:Page {id: id})
CREATE (c)-[:CITES]->(p);
UNWIND $recipes AS r
CREATE (k:CookbookRecipe {id: r.id, title: r.title, url: r.url})
WITH k, r
UNWIND r.demonstrates AS id
MATCH (c:Capability {id: id})
CREATE (k)-[:DEMONSTRATES]->(c);
UNWIND $capabilities AS r
MATCH (c:Capability {id: r.id})
UNWIND r.verified_by AS v
MATCH (x:CookbookRecipe|Page {id: v.id})
WHERE v.label IN labels(x)
CREATE (c)-[:VERIFIED_BY {cell: v.cell}]->(x);
UNWIND $sections AS r
MATCH (p:Page {id: r.page})
CREATE (p)-[:HAS_SECTION]->(:Section {id: r.id, page: r.page, heading: r.heading,
position: r.position, url: r.url, text: r.text,
embedding: vector(r.e, 384, FLOAT32)});
UNWIND $questions AS r
CREATE (:Question {text: r.q, embedding: vector(r.e, 384, FLOAT32)});
RETURN COUNT { MATCH (:Page) } AS pages, COUNT { MATCH (:Section) } AS sections,
COUNT { MATCH (:Term) } AS terms, COUNT { MATCH (:Capability) } AS capabilities,
COUNT { MATCH (:CookbookRecipe) } AS recipes,
COUNT { MATCH ()-[]->() } AS relationships| pages | sections | terms | capabilities | recipes | relationships |
|---|---|---|---|---|---|
| 142 | 75 | 65 | 20 | 11 | 2547 |
1 row · nodes created: 453, relationships created: 2547, properties set: 1645, labels added: 453
| pages | sections | terms | capabilities | recipes | relationships | |-------|----------|-------|--------------|---------|---------------| | 142 | 75 | 65 | 20 | 11 | 2547 | 1 row · nodes created: 453, relationships created: 2547, properties set: 1645, labels added: 453
Full-text search matches words
Full-text SEARCH splits the question into lowercase terms and ORs them. The
score is BM25: a term that few sections hold counts for more, and a match in
a short section counts for more than one in a long section. There is no
stemming, and common words such as the and from count too. The page that
answers this question,
"Keep an agent safe", says it in other words: read-only stores, safe mode,
permissions.
MATCH (s:Section)
SEARCH s IN (FULLTEXT INDEX docs FOR $q LIMIT $k) SCORE AS score
RETURN s.page AS page, s.heading AS heading, score
ORDER BY score DESC, s.idParameters
| page | heading | score |
|---|---|---|
| "guides/cypher/vector-search" | "Prerequisites" | 2.9741323045141 |
| "reference/api/python" | "ResultGraph" | 2.2898271927135236 |
| "guides/cypher/vector-search" | "Find the nearest nodes" | 2.1949869909764943 |
| "guides/cypher/vector-search" | "Search by vector similarity" | 1.764290491759474 |
| "guides/agents/agent-safety" | "Back up before a write" | 1.7070002894276977 |
5 rows
| page | heading | score | |-------------------------------|-------------------------------|--------------------| | "guides/cypher/vector-search" | "Prerequisites" | 2.9741323045141 | | "reference/api/python" | "ResultGraph" | 2.2898271927135236 | | "guides/cypher/vector-search" | "Find the nearest nodes" | 2.1949869909764943 | | "guides/cypher/vector-search" | "Search by vector similarity" | 1.764290491759474 | | "guides/agents/agent-safety" | "Back up before a write" | 1.7070002894276977 | 5 rows
One of the five, the backup section, comes from "Keep an agent safe"; the other four share the question's words, not its meaning.
Vector search finds the entry points
The vector search starts from the question's stored embedding. SEARCH
binds s to the k nearest sections; the score is the cosine similarity
rescaled to 0 to 1, so higher is closer.
MATCH (question:Question {text: $q})
MATCH (s:Section)
SEARCH s IN (VECTOR INDEX docs_vector FOR question.embedding LIMIT $k)
SCORE AS score
RETURN s.page AS page, s.heading AS heading, round(score, 4) AS score
ORDER BY score DESC, s.idParameters
| page | heading | score |
|---|---|---|
| "guides/agents/agent-safety" | "Verify" | 0.8147 |
| "guides/agents/agent-safety" | "Turn on safe mode" | 0.8067 |
| "guides/agents/agent-safety" | "Permission matrix" | 0.8035 |
| "reference/api/python" | "Open options" | 0.8028 |
| "guides/agents/agent-safety" | "Control network egress" | 0.7987 |
5 rows
| page | heading | score | |------------------------------|--------------------------|--------| | "guides/agents/agent-safety" | "Verify" | 0.8147 | | "guides/agents/agent-safety" | "Turn on safe mode" | 0.8067 | | "guides/agents/agent-safety" | "Permission matrix" | 0.8035 | | "reference/api/python" | "Open options" | 0.8028 | | "guides/agents/agent-safety" | "Control network egress" | 0.7987 | 5 rows
Four of the five sections come from "Keep an agent safe". The fifth is the
Python API's "Open options", whose read_only option refuses writes. These
are the entry points; the next step starts from them.
Expand the entry points into context
A section is a fragment. Before a model can answer from it, it needs the
page the section belongs to, what a reader should know first, and the words
the page assumes. This step groups the nearest sections by page, then walks
back along PREREQUISITE_OF up to three hops, a quantified path pattern,
for the pages to read first, farthest first, and follows USES_TERM to the
glossary terms with their short definitions. The rows are the context an
agent would pass to a model with the sections' text.
MATCH (question:Question {text: $q})
MATCH (s:Section)
SEARCH s IN (VECTOR INDEX docs_vector FOR question.embedding LIMIT $k)
SCORE AS score
MATCH (p:Page)-[:HAS_SECTION]->(s)
WITH p, s, score ORDER BY score DESC, s.id
WITH p, collect(s.heading) AS sections, max(score) AS best
RETURN p.title AS page, sections, round(best, 4) AS score,
COLLECT { MATCH path = (pre:Page)-[:PREREQUISITE_OF]->{1,3}(p)
WITH pre, max(length(path)) AS steps
RETURN pre.title ORDER BY steps DESC, pre.title } AS read_first,
COLLECT { MATCH (p)-[:USES_TERM]->(t:Term)
RETURN t.term + ': ' + t.short ORDER BY t.id } AS terms
ORDER BY score DESC, pageParameters
| page | sections | score | read_first | terms |
|---|---|---|---|---|
| "Keep an agent safe" | ["Verify", "Turn on safe mode", "Permission matrix", "Control network egress"] | 0.8147 | ["Install relate"] | ["checkpoint: The step that writes a fresh snapshot of committed state and recycles old WAL segments: every 300 seconds by default, on clean exit, and on `.checkpoint`.", "MCP: The Model Context Protocol, which agent hosts use to call tools; `relate serve --mcp` exposes a store as `read_cypher`, `get_schema` and `write_cypher`.", "read-only: An open mode (`--read-only`) that never writes, checkpoints or migrates the store; writes fail, and a directory store can be read beside its writer.", "safe mode: An opt-in policy (`--safe`) that refuses a new named node without a label, an unbounded `MATCH` read and a remote `--load`, before anything runs.", "store: A relate database: a directory, a single `.rl` file, or `:memory:`, opened with `--database`."] |
| "Python API" | ["Open options"] | 0.8028 | [] | ["client: A language package that runs the relate engine inside a program's process: the Python, Node.js, Rust, Java, Go and Swift clients.", "node: An entity in the graph: it carries zero or more labels and a map of properties, and relationships connect it to other nodes.", "path: An alternating sequence of nodes and relationships that a pattern matched, bound with `p = …` and read with `nodes(p)`, `relationships(p)` and `length(p)`.", "relationship: A directed, typed connection between two nodes, with properties of its own; every relationship has exactly one type and a start and an end node.", "store: A relate database: a directory, a single `.rl` file, or `:memory:`, opened with `--database`."] |
2 rows
| page | sections | score | read_first | terms | |----------------------|--------------------------------------------------------------------------------|--------|--------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | "Keep an agent safe" | ["Verify", "Turn on safe mode", "Permission matrix", "Control network egress"] | 0.8147 | ["Install relate"] | ["checkpoint: The step that writes a fresh snapshot of committed state and recycles old WAL segments: every 300 seconds by default, on clean exit, and on `.checkpoint`.", "MCP: The Model Context Protocol, which agent hosts use to call tools; `relate serve --mcp` exposes a store as `read_cypher`, `get_schema` and `write_cypher`.", "read-only: An open mode (`--read-only`) that never writes, checkpoints or migrates the store; writes fail, and a directory store can be read beside its writer.", "safe mode: An opt-in policy (`--safe`) that refuses a new named node without a label, an unbounded `MATCH` read and a remote `--load`, before anything runs.", "store: A relate database: a directory, a single `.rl` file, or `:memory:`, opened with `--database`."] | | "Python API" | ["Open options"] | 0.8028 | [] | ["client: A language package that runs the relate engine inside a program's process: the Python, Node.js, Rust, Java, Go and Swift clients.", "node: An entity in the graph: it carries zero or more labels and a map of properties, and relationships connect it to other nodes.", "path: An alternating sequence of nodes and relationships that a pattern matched, bound with `p = …` and read with `nodes(p)`, `relationships(p)` and `length(p)`.", "relationship: A directed, typed connection between two nodes, with properties of its own; every relationship has exactly one type and a start and an end node.", "store: A relate database: a directory, a single `.rl` file, or `:memory:`, opened with `--database`."] | 2 rows
The four sections of "Keep an agent safe" become one row: read "Install
relate" first, and know five terms, from checkpoint to store. The Python API
has no prerequisite in the graph, so its read_first is empty: a
COLLECT { … } subquery gives each row a list, empty or not, and never
drops the row.
Ask your own question
Change q to a question of your own and the vector search returns no rows:
only the two questions in the data have a stored vector. A question's vector
is only comparable with vectors from the same model, so a new question needs
bge-small-en-v1.5 at the revision and pooling the sections were embedded
with. In the browser, this element downloads that model, embeds the question
in the tab, and runs the context step with the new vector in place of the
stored one, so the answer comes back with its pages, prerequisites and terms.
The element embeds a typed question with the model that made these vectors: bge-small-en-v1.5 at revision ea104da, with CLS pooling, normalized, as its half-precision ONNX weights. The button downloads 82 MB: the weights (66.7 MB) and the tokenizer (0.7 MB) from huggingface.co, and the ONNX runtime (14.2 MB) from this site. Nothing downloads before the press, and each file is checked against its SHA-256 before it is used. The question, at most 200 characters, is embedded in the browser tab and never sent anywhere.
Step context then runs with its first line, MATCH (question:Question {text: $q}), replaced by WITH {embedding: vector($embedding, 384, FLOAT32)} AS question, so question.embedding is the typed question's vector and every other line runs as written.
Step context's recorded result, for the stored question "stop the model from deleting nodes":
| page | sections | score | read_first | terms |
|---|---|---|---|---|
| Keep an agent safe | ["Verify","Turn on safe mode","Permission matrix","Control network egress"] | 0.8147 | ["Install relate"] | ["checkpoint: The step that writes a fresh snapshot of committed state and recycles old WAL segments: every 300 seconds by default, on clean exit, and on .checkpoint.","MCP: The Model Context Protocol, which agent hosts use to call tools; relate serve --mcp exposes a store as read_cypher, get_schema and write_cypher.","read-only: An open mode (--read-only) that never writes, checkpoints or migrates the store; writes fail, and a directory store can be read beside its writer.","safe mode: An opt-in policy (--safe) that refuses a new named node without a label, an unbounded MATCH read and a remote --load, before anything runs.","store: A relate database: a directory, a single .rl file, or :memory:, opened with --database."] |
| Python API | ["Open options"] | 0.8028 | [] | ["client: A language package that runs the relate engine inside a program's process: the Python, Node.js, Rust, Java, Go and Swift clients.","node: An entity in the graph: it carries zero or more labels and a map of properties, and relationships connect it to other nodes.","path: An alternating sequence of nodes and relationships that a pattern matched, bound with p = … and read with nodes(p), relationships(p) and length(p).","relationship: A directed, typed connection between two nodes, with properties of its own; every relationship has exactly one type and a start and an end node.","store: A relate database: a directory, a single .rl file, or :memory:, opened with --database."] |
Live: a button that states the download size fetches the model's weights and tokenizer from huggingface.co and the ONNX runtime from this site, checks each file's SHA-256, embeds the question you type in your browser (the question is never sent anywhere), and runs the step's vector search with that vector in place of the stored one on the page's database in your browser; nothing downloads before the press.
Elsewhere, embed the question with the code in "Where the data comes from"
and pass the vector as a parameter (FOR $v). The full-text search works on
any text.
A word with two meanings
The second question names an editor, Cursor. The Python API reference has a class of the same name, a database cursor, and the question's vector lands nearest to it. This step runs the plain vector search and adds one hop per hit: the surfaces its page applies to.
MATCH (question:Question {text: $q})
MATCH (s:Section)
SEARCH s IN (VECTOR INDEX docs_vector FOR question.embedding LIMIT $k)
SCORE AS score
MATCH (p:Page)-[:HAS_SECTION]->(s)
RETURN p.title AS page, s.heading AS heading, round(score, 4) AS score,
COLLECT { MATCH (p)-[:APPLIES_TO]->(x:Surface)
RETURN x.id ORDER BY x.id } AS surfaces
ORDER BY score DESC, s.idParameters
| page | heading | score | surfaces |
|---|---|---|---|
| "Python API" | "Cursor" | 0.8454 | ["python"] |
| "Connect over MCP" | "Cursor" | 0.8432 | ["agents", "cli", "mcp"] |
| "Python API" | "ResultGraph" | 0.8089 | ["python"] |
| "Python API" | "Open options" | 0.8055 | ["python"] |
| "Keep an agent safe" | "Check destructive statements before they run" | 0.8036 | ["agents", "cli", "mcp"] |
5 rows
| page | heading | score | surfaces | |----------------------|------------------------------------------------|--------|--------------------------| | "Python API" | "Cursor" | 0.8454 | ["python"] | | "Connect over MCP" | "Cursor" | 0.8432 | ["agents", "cli", "mcp"] | | "Python API" | "ResultGraph" | 0.8089 | ["python"] | | "Python API" | "Open options" | 0.8055 | ["python"] | | "Keep an agent safe" | "Check destructive statements before they run" | 0.8036 | ["agents", "cli", "mcp"] | 5 rows
The Python API's Cursor ranks first, and three of the five sections come
from that page, the only one among them that applies to python and not to
mcp.
Search only where the graph points
When the graph knows where to look, the search can start there. A reader
connecting Cursor works over MCP, so this step walks from the mcp surface
to the pages that apply to it, then runs the vector search over those pages'
sections only. WHERE s.page IN scope inside the SEARCH filters before
the top k are chosen, so the search still returns k sections. A vector
search may filter only on a property its index declares, which is what
WITH [s.page] on docs_vector is for.
MATCH (question:Question {text: $q})
MATCH (:Surface {id: $surface})<-[:APPLIES_TO]-(p:Page)
WITH question, collect(p.id) AS scope
MATCH (s:Section)
SEARCH s IN (VECTOR INDEX docs_vector FOR question.embedding
WHERE s.page IN scope LIMIT $k) SCORE AS score
RETURN s.page AS page, s.heading AS heading, round(score, 4) AS score
ORDER BY score DESC, s.idParameters
| page | heading | score |
|---|---|---|
| "guides/agents/connect-over-mcp" | "Cursor" | 0.8432 |
| "guides/agents/agent-safety" | "Check destructive statements before they run" | 0.8036 |
| "guides/agents/connect-over-mcp" | "Windsurf" | 0.8007 |
| "guides/agents/agent-safety" | "Open the store read-only" | 0.8006 |
| "guides/agents/agent-safety" | "Keep an agent safe" | 0.7974 |
5 rows
| page | heading | score | |----------------------------------|------------------------------------------------|--------| | "guides/agents/connect-over-mcp" | "Cursor" | 0.8432 | | "guides/agents/agent-safety" | "Check destructive statements before they run" | 0.8036 | | "guides/agents/connect-over-mcp" | "Windsurf" | 0.8007 | | "guides/agents/agent-safety" | "Open the store read-only" | 0.8006 | | "guides/agents/agent-safety" | "Keep an agent safe" | 0.7974 | 5 rows
Thirty pages apply to mcp, and two of them, the agent guides, have
sections in this data. The Python sections are gone; the five left are the
Cursor setup, the check on destructive statements, Windsurf, the read-only
open, and the safety page's introduction.
Note A
WHEREafter theSEARCHclause filters only the rows the search returned, so it can return fewer thank. WithoutWITH [s.page]on the index, the filter inside the search fails withWrongPropertyForVectorSearchFilter.
Walk from the answer to its worked example
The capability catalog says what relate is for: each capability states a
problem, cites the pages that explain it, and names the recipe or page step
that proves it. This step follows CITES back from the scoped hits' pages,
counts the hits under each capability, and returns the top three with the
problem each solves, the recipes that demonstrate it, and what verifies it.
MATCH (question:Question {text: $q})
MATCH (:Surface {id: $surface})<-[:APPLIES_TO]-(p:Page)
WITH question, collect(p.id) AS scope
MATCH (s:Section)
SEARCH s IN (VECTOR INDEX docs_vector FOR question.embedding
WHERE s.page IN scope LIMIT $k) SCORE AS score
MATCH (c:Capability)-[:CITES]->(:Page)-[:HAS_SECTION]->(s)
WITH c, count(s) AS hits
MATCH (c)-[:SOLVES]->(problem:Problem {kind: 'problem'})
RETURN c.title AS capability, hits, problem.text AS problem,
COLLECT { MATCH (r:CookbookRecipe)-[:DEMONSTRATES]->(c)
RETURN r.title ORDER BY r.title } AS demonstrated_by,
COLLECT { MATCH (c)-[v:VERIFIED_BY]->(x)
RETURN x.title + ' (' + v.cell + ')' ORDER BY x.title } AS verified_by
ORDER BY hits DESC, capability LIMIT 3Parameters
| capability | hits | problem | demonstrated_by | verified_by |
|---|---|---|---|---|
| "Graph queries over MCP" | 5 | "Give a coding agent or an agent framework a graph it can query through MCP, without letting it change the data." | [] | ["Use relate with the Claude Agent SDK (options)"] |
| "Migration rehearsal" | 3 | "Check that a data migration changed only what it was meant to, before it runs on the real graph." | ["Rehearse a migration against a golden graph"] | ["Rehearse a migration against a golden graph (verdict)"] |
| "Offline and sandboxed use" | 3 | "Run a graph database where an agent works isolated, with no network after setup and nothing sent anywhere." | [] | ["Run relate in a sandbox (step-1)"] |
3 rows
| capability | hits | problem | demonstrated_by | verified_by | |-----------------------------|------|-------------------------------------------------------------------------------------------------------------------|-------------------------------------------------|-----------------------------------------------------------| | "Graph queries over MCP" | 5 | "Give a coding agent or an agent framework a graph it can query through MCP, without letting it change the data." | [] | ["Use relate with the Claude Agent SDK (options)"] | | "Migration rehearsal" | 3 | "Check that a data migration changed only what it was meant to, before it runs on the real graph." | ["Rehearse a migration against a golden graph"] | ["Rehearse a migration against a golden graph (verdict)"] | | "Offline and sandboxed use" | 3 | "Run a graph database where an agent works isolated, with no network after setup and nothing sent anywhere." | [] | ["Run relate in a sandbox (step-1)"] | 3 rows
"Graph queries over MCP" cites the pages of all five hits, and the problem it
solves restates the question: a graph a coding agent can query through MCP
without changing the data. No recipe demonstrates it; the catalog verifies
it with the step named options on the Claude Agent SDK guide. The next two
cite "Keep an agent safe" for other reasons, rehearsing a migration and
running offline, and the first of them has a recipe.
The whole graph
The same statements run on the whole docs graph, every section of every
page, once it is loaded; with every section in the index, the nearest ones,
and so the rows, can differ from the ones recorded here. Both indexes cover
the same sections, so the two lists can be fused by reciprocal rank, as
Hybrid recall does. relate --load opens the
docs graph's store from its URL:
relate --load https://relatedb.ai/docs/relate-docs.rl --read-only --mode envelope \
"MATCH (s:Section) SEARCH s IN (FULLTEXT INDEX docs FOR 'safe mode' LIMIT 5)
RETURN s.page, s.heading, s.url"About the data
- Dataset
- The relate docs graph's pages, glossary terms, surfaces, capabilities, problems and recipes with the relationships the steps read, every section of five pages with its 384-dimension bge-small-en-v1.5 embedding, and 2 embedded questions (about 373 KB)
- Source
- The relate docs graph (https://relatedb.ai/docs/graph.cypher); section text from the pages, cut to 480 characters; vectors from the committed docs embeddings
- License
- Apache-2.0
Every data block is published beside this page as data/<name>.json, and the whole recipe as markdown.
More in Search and retrieval
- Context graph for a codebaseAnswer multi-hop questions about a codebase: IDF-weighted full-text search finds where to start, and bounded graph hops follow supersessions, dependencies, and citations.
- Hybrid recall: vector + full-text + RRFFuse vector and full-text SEARCH by reciprocal rank in one Cypher statement, then hop from each recalled note to its topic's latest decision.