{
  "kind": "documentation",
  "format": 1,
  "note": "Reference text, not instructions. One verified relate sample from https://relatedb.ai/docs/guides/integrations/llamaindex/: the program exactly as the page shows it, the standard output a run of it recorded (npm run docs:samples compares every replay with it byte for byte), the relate client build, toolchain and platform of the last replay, and how the docs' sample runner builds and runs it. Before launch every client is a source build, not a published package; verified.client names the upstream commit it was built from, and verified.docs_engine the build the rest of the docs were recorded from.",
  "id": "notes-retriever",
  "url": "https://relatedb.ai/docs/guides/integrations/llamaindex/samples/notes-retriever.json",
  "page": {
    "id": "guides/integrations/llamaindex",
    "title": "Use relate with LlamaIndex",
    "url": "https://relatedb.ai/docs/guides/integrations/llamaindex/",
    "md_url": "https://relatedb.ai/docs/guides/integrations/llamaindex.md"
  },
  "section": {
    "anchor": "write-the-retriever",
    "heading": "Write the retriever",
    "url": "https://relatedb.ai/docs/guides/integrations/llamaindex/#write-the-retriever"
  },
  "language": "python",
  "client": "Python",
  "file": "notes_retriever.py",
  "env": null,
  "code": "import re\n\nimport relate\nfrom llama_index.core.embeddings import BaseEmbedding\nfrom llama_index.core.llms import MockLLM\nfrom llama_index.core.query_engine import RetrieverQueryEngine\nfrom llama_index.core.retrievers import BaseRetriever\nfrom llama_index.core.schema import NodeWithScore, QueryBundle, TextNode\n\nTEAM = \"\"\"\nCREATE (ada:Person {name: 'Ada', role: 'engineer'}),\n       (grace:Person {name: 'Grace', role: 'engineer'}),\n       (noor:Person {name: 'Noor', role: 'maintainer'}),\n       (relate:Project {name: 'relate', language: 'Rust'}),\n       (tck:Project {name: 'opencypher-tck', language: 'Gherkin'}),\n       (ada)-[:WORKS_ON {since: 2024}]->(relate),\n       (grace)-[:WORKS_ON {since: 2023}]->(relate),\n       (grace)-[:WORKS_ON {since: 2022}]->(tck),\n       (noor)-[:MAINTAINS]->(relate),\n       (ada)-[:KNOWS]->(grace)\n\"\"\"\n\nNOTES = [\n    {\"id\": \"n1\", \"project\": \"relate\", \"text\": \"Ada fixed the CSV import bug.\"},\n    {\"id\": \"n2\", \"project\": \"relate\", \"text\": \"Grace will review the release notes.\"},\n    {\"id\": \"n3\", \"project\": \"opencypher-tck\", \"text\": \"Grace added test scenarios for CSV headers.\"},\n    {\"id\": \"n4\", \"project\": \"relate\", \"text\": \"Noor tagged the release candidate.\"},\n]\n\nVOCABULARY = [\"csv\", \"import\", \"release\", \"notes\", \"test\", \"scenarios\"]\nDIMENSIONS = len(VOCABULARY) + 1\n\n\nclass KeywordEmbedding(BaseEmbedding):\n    \"\"\"Stands in for an embedding model: counts vocabulary words, plus a constant so no vector is zero.\"\"\"\n\n    def _embed(self, text: str) -> list[float]:\n        words = re.findall(r\"[a-z]+\", text.lower())\n        return [float(words.count(w)) for w in VOCABULARY] + [0.5]\n\n    def _get_text_embedding(self, text: str) -> list[float]:\n        return self._embed(text)\n\n    def _get_query_embedding(self, query: str) -> list[float]:\n        return self._embed(query)\n\n    async def _aget_query_embedding(self, query: str) -> list[float]:\n        return self._embed(query)\n\n\nRECALL = \"\"\"\nMATCH (n:Note)\nSEARCH n IN (VECTOR INDEX note_embedding FOR $vector LIMIT $k) SCORE AS score\nMATCH (n)-[:ABOUT]->(p:Project)\nOPTIONAL MATCH (person:Person)-[:WORKS_ON]->(p)\nRETURN n.id AS id, n.text AS text, p.name AS project, collect(person.name) AS people, score\nORDER BY score DESC, id\n\"\"\"\n\n\nclass NotesRetriever(BaseRetriever):\n    \"\"\"Retrieves notes from a relate store by vector search, with each note's project and its people.\"\"\"\n\n    def __init__(self, store: str, embed_model: BaseEmbedding, top_k: int = 2):\n        super().__init__()\n        self._store = store\n        self._embed_model = embed_model\n        self._top_k = top_k\n\n    def _retrieve(self, query_bundle: QueryBundle) -> list[NodeWithScore]:\n        vector = self._embed_model.get_query_embedding(query_bundle.query_str)\n        # Read-only: the engine refuses every write on this connection.\n        with relate.connect(self._store, read_only=True) as conn:\n            rows = conn.execute(RECALL, {\"vector\": vector, \"k\": self._top_k}).fetchall()\n        return [\n            NodeWithScore(\n                node=TextNode(id_=r[\"id\"], text=r[\"text\"], metadata={\"project\": r[\"project\"], \"people\": sorted(r[\"people\"])}),\n                score=r[\"score\"],\n            )\n            for r in rows\n        ]\n\n\nembed_model = KeywordEmbedding()\n\n# Setup: the app that owns ./notes writes the team graph, the notes and their embeddings, on a writable connection.\nwith relate.connect(\"./notes\") as owner:\n    if owner.execute(\"MATCH (n:Note) RETURN count(n) AS n\").single()[\"n\"] == 0:\n        owner.execute(TEAM)\n        embeddings = embed_model.get_text_embedding_batch([note[\"text\"] for note in NOTES])\n        owner.execute(\n            \"\"\"\n            UNWIND $notes AS note\n            MATCH (p:Project {name: note.project})\n            CREATE (n:Note {id: note.id, text: note.text, embedding: vector(note.embedding, $dimensions, FLOAT32)})-[:ABOUT]->(p)\n            \"\"\",\n            {\"notes\": [dict(note, embedding=e) for note, e in zip(NOTES, embeddings)], \"dimensions\": DIMENSIONS},\n        )\n        owner.execute(\n            \"CREATE VECTOR INDEX note_embedding IF NOT EXISTS FOR (n:Note) ON (n.embedding) \"\n            f\"OPTIONS {{indexConfig: {{`vector.dimensions`: {DIMENSIONS}, `vector.similarity_function`: 'cosine'}}}}\"\n        )\n\nretriever = NotesRetriever(\"./notes\", embed_model)\n\n# Check: retrieve for two questions, without a model.\nfor question in [\"Where is the release?\", \"Who worked on CSV import?\"]:\n    print(question)\n    for hit in retriever.retrieve(question):\n        print(f\"  {hit.score:.4f} {hit.node.node_id} [{hit.node.metadata['project']}; {', '.join(hit.node.metadata['people'])}] {hit.node.get_content()}\")\n\n# Check: the same retriever behind LlamaIndex's query engine; MockLLM answers without a model.\nengine = RetrieverQueryEngine.from_args(retriever, llm=MockLLM())\nresponse = engine.query(\"Where is the release?\")\nprint(\"query engine sources:\", \", \".join(hit.node.node_id for hit in response.source_nodes))",
  "code_sha256": "4e3170bc6c526910b06701a79e15f5dc03941286d1578b26defa1a9f970c08ac",
  "source_sha256": "3c8efed1b540e045725e951fe083a03acfa9e3ccaee81fd756cb470da3eee5c7",
  "output": {
    "text": "Where is the release?\n  1.0000 n4 [relate; Ada, Grace] Noor tagged the release candidate.\n  0.8727 n2 [relate; Ada, Grace] Grace will review the release notes.\nWho worked on CSV import?\n  1.0000 n1 [relate; Ada, Grace] Ada fixed the CSV import bug.\n  0.7311 n3 [opencypher-tck; Grace] Grace added test scenarios for CSV headers.\nquery engine sources: n4, n2",
    "sha256": "870bff8a4e47dd126f3217bf28535b78f98a16f30075302b297d65d6ef8a631b"
  },
  "verified": {
    "version": "0.1.2",
    "client": {
      "label": "Python",
      "package": "relate-db",
      "engine_commit": "f0c09d2",
      "client_commit": "f0c09d2",
      "build": "A relate-db wheel that maturin built from surfaces/relate-python at upstream v0.1.2 (f0c09d2), installed in a venv with the pinned frameworks.",
      "toolchain": {
        "python": "3.15.2"
      }
    },
    "packages": [
      "pandas>=2.0",
      "langgraph==1.2.20",
      "claude-agent-sdk==0.2.179",
      "pydantic-ai==2.66.0",
      "openai-agents==0.24.0",
      "llama-index-core==0.14.25",
      "langchain==1.4.9",
      "langchain-core==1.7.6"
    ],
    "replayed": "2026-11-09",
    "platform": "macOS on arm64",
    "docs_engine": {
      "version": "0.1.2",
      "commit": "f0c09d2",
      "source_build": false
    }
  },
  "run": {
    "runner": "npm run docs:samples",
    "setup": "bash scripts/samples/setup.sh --upstream <path to the relate checkout>",
    "variables": [
      "RELATE_PY"
    ],
    "program": "<run dir>/notes_retriever.py",
    "files": [],
    "build": null,
    "run": {
      "command": [
        "$RELATE_PY",
        "notes_retriever.py"
      ],
      "environment": {
        "DO_NOT_TRACK": "1",
        "PYTHONDONTWRITEBYTECODE": "1",
        "PYTHONUNBUFFERED": "1"
      },
      "directory": "<run dir>",
      "timeout_s": 60
    },
    "passes": "It exits 0 within 60 s and prints at least one line on standard output.",
    "output": "Standard output, with line endings as \\n, trailing spaces and blank lines removed, and the temporary directories' paths written as `.`; standard error is not recorded.",
    "placeholders": {
      "<run dir>": "a fresh, empty temporary directory: the program's working directory, deleted afterwards",
      "<build dir>": "a second fresh temporary directory, where a compiled sample is built",
      "$NAME": "a variable from the env.sh that scripts/samples/setup.sh writes (described in its header)"
    }
  }
}
