{"id":79584,"date":"2026-10-28T07:51:44","date_gmt":"2026-10-28T07:51:44","guid":{"rendered":"https:\/\/www.devopsschool.com\/blog\/?p=79584"},"modified":"2026-10-28T07:51:45","modified_gmt":"2026-10-28T07:51:45","slug":"neo4j-graphrag-master-guide","status":"publish","type":"post","link":"https:\/\/www.devopsschool.com\/blog\/neo4j-graphrag-master-guide\/","title":{"rendered":"Neo4j GraphRAG Master Guide"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">Github Ref &#8211; <a href=\"https:\/\/github.com\/AiOpsSchool\/neo4j-graphrag-master-guide\">https:\/\/github.com\/AiOpsSchool\/neo4j-graphrag-master-guide<\/a><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Chapters 1-4 | Neo4j GraphRAG Master Guide | Research date: 2026-10-21<\/strong><\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\"><strong>Scope:<\/strong> This is the authored foundations volume. It deliberately does not claim to implement all of GraphRAG. The first reproducible lab stores and queries a conventional property graph without an LLM. This is essential groundwork for the later vector, extraction, and RAG chapters.<\/p>\n<\/blockquote>\n\n\n\n<h2 class=\"wp-block-heading\">Chapter 1 &#8211; Introduction to GraphRAG<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">1.1 Learning objectives<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">By the end of this chapter, you should be able to define GraphRAG, explain the difference between graph storage and graph-augmented retrieval, compare architectures, identify appropriate use cases, and specify a measurement plan before choosing a graph architecture.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">1.2 Why this matters<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">A language model produces fluent text but does not inherently know your current systems, incidents, employees, policies, or business data. Standard RAG addresses knowledge freshness by retrieving snippets at question time. That is often enough for a question such as &#8220;What is our password rotation policy?&#8221; It is less reliable for a question like &#8220;If identity-service is down, which user-facing services depend on it through two or more layers?&#8221; The second question needs relationships and traversal, not only semantically similar paragraphs.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">GraphRAG is not one universally standardized algorithm. In this book, <strong>GraphRAG<\/strong> means <em>retrieval-augmented generation that uses a graph or knowledge-graph representation as a meaningful part of selecting, expanding, organizing, or verifying evidence for an LLM<\/em>. It can start from a vector search result, resolve entities, follow bounded relationships, and include corroborating source passages in the model&#8217;s context. Some workloads use structured Cypher as the primary retrieval mechanism instead of vector search.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Neo4j provides a property-graph database, the Cypher query language, indexes, graph traversals, and a first-party GraphRAG Python package with multiple retrievers and generation integration. Knowledge graph construction functions in the official Python package are explicitly marked experimental as of this edition. <a href=\"https:\/\/neo4j.com\/docs\/neo4j-graphrag-python\/current\/\">Neo4j GraphRAG docs<\/a>, <a href=\"https:\/\/neo4j.com\/docs\/neo4j-graphrag-python\/current\/user_guide_kg_builder.html\">KG Builder docs<\/a>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">1.3 Core vocabulary<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>LLM (large language model):<\/strong> a generative model that predicts and produces text or structured output from prompts.<\/li>\n\n\n\n<li><strong>Retrieval:<\/strong> finding potentially useful evidence for the question.<\/li>\n\n\n\n<li><strong>Context assembly:<\/strong> selecting, formatting and limiting evidence sent to the LLM.<\/li>\n\n\n\n<li><strong>Knowledge graph:<\/strong> entities\/facts plus explicit relations and often a governing schema or vocabulary.<\/li>\n\n\n\n<li><strong>Property graph:<\/strong> nodes and directed relationships that may have labels\/types and properties.<\/li>\n\n\n\n<li><strong>GraphRAG:<\/strong> a question-answering pipeline using graph relations for retrieval or evidence organization.<\/li>\n\n\n\n<li><strong>Provenance:<\/strong> where a claim came from (document\/record, location, version and time).<\/li>\n\n\n\n<li><strong>Grounded answer:<\/strong> an answer whose material assertions are supported by authorized retrieved evidence.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">1.4 GraphRAG versus alternative approaches<\/h3>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>Approach<\/th><th>What it changes<\/th><th>Strongest uses<\/th><th>Limitations<\/th><\/tr><\/thead><tbody><tr><td>Long-context prompting<\/td><td>Puts more source text in one prompt<\/td><td>A small bounded set of documents<\/td><td>Input cost, distracting content, changing sources, no structural guarantees<\/td><\/tr><tr><td>Fine-tuning<\/td><td>Changes model behavior or learned distribution<\/td><td>Style, classifications, repeated format constraints<\/td><td>Not a fresh authoritative fact store or automatic access-control system<\/td><\/tr><tr><td>Lexical RAG<\/td><td>Retrieves exact terms through full-text indexes<\/td><td>IDs, error codes, names<\/td><td>Paraphrases and missing explicit relations<\/td><\/tr><tr><td>Vector RAG<\/td><td>Retrieves similar text embeddings<\/td><td>Semantic similarity, unstructured corpora<\/td><td>May miss multi-hop dependencies or strict relationships<\/td><\/tr><tr><td>Knowledge graph without an LLM<\/td><td>Structured graph query<\/td><td>Deterministic topology and relationship questions<\/td><td>Does not itself generate natural-language synthesis<\/td><\/tr><tr><td>GraphRAG<\/td><td>Uses graphs in the evidence retrieval and assembly path<\/td><td>Multi-hop and relationship-aware answers with textual explanation<\/td><td>Additional modeling, ingestion, inference and operations complexity<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Importantly, <strong>GraphRAG is not synonymous with a knowledge graph<\/strong>. A knowledge graph can power analytics or operational queries without an LLM. GraphRAG is an application pattern that combines graph-derived information with retrieval-augmented answer generation. Conversely, GraphRAG need not replace lexical\/vector search; it often complements them.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">1.5 Running example: a dependency question<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Northstar Labs has six fictional services:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\">api-gateway --&gt; checkout --&gt; payments --&gt; auth\n       |           |                     ^\n       +----------&gt;auth                   |\n                   +-----&gt; inventory      |\nnotification ----------------------------&gt; payments (dependency)<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">The plain text diagram is schematic; the actual directed edges in Lab 01 are stored as data. If the user asks, &#8220;Which services depend directly or indirectly on payments?&#8221;, full-text document search may find a payments runbook and still omit an upstream consumer. A graph query can traverse the <code>DEPENDS_ON<\/code> edges in reverse to enumerate affected consumers within an explicit depth bound. A separate retrieval step should then find incident evidence and runbooks before composing an answer. A graph query alone does not establish actual service downtime: a dependency is a <em>potential impact path<\/em>, and runtime resilience, caches and fallbacks matter.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">A conceptual answer assembly could contain:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">Question: Which systems could be affected if payments fails?\nGraph paths: checkout -&gt; payments;\n             api-gateway -&gt; checkout -&gt; payments;\n             notification -&gt; payments.\nSource evidence: Incident INC-001; Runbook RB-001.\nAnswer: Three potential upstream consumers were found in the modeled\n        dependency graph. This is not proof of live impact.<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\"><strong>This output is illustrative, not an executed or measured answer.<\/strong><\/p>\n\n\n\n<h3 class=\"wp-block-heading\">1.6 GraphRAG lifecycle<\/h3>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-tag\">flowchart<\/span> <span class=\"hljs-selector-tag\">LR<\/span>\n  <span class=\"hljs-selector-tag\">S<\/span><span class=\"hljs-selector-attr\">&#91;Sources]<\/span> <span class=\"hljs-selector-tag\">--<\/span>&gt; <span class=\"hljs-selector-tag\">P<\/span><span class=\"hljs-selector-attr\">&#91;Parse and chunk]<\/span>\n  <span class=\"hljs-selector-tag\">P<\/span> <span class=\"hljs-selector-tag\">--<\/span>&gt; <span class=\"hljs-selector-tag\">X<\/span><span class=\"hljs-selector-attr\">&#91;Extract and resolve]<\/span>\n  <span class=\"hljs-selector-tag\">X<\/span> <span class=\"hljs-selector-tag\">--<\/span>&gt; <span class=\"hljs-selector-tag\">K<\/span><span class=\"hljs-selector-attr\">&#91;(Knowledge graph)]<\/span>\n  <span class=\"hljs-selector-tag\">P<\/span> <span class=\"hljs-selector-tag\">--<\/span>&gt; <span class=\"hljs-selector-tag\">E<\/span><span class=\"hljs-selector-attr\">&#91;Embed chunks]<\/span>\n  <span class=\"hljs-selector-tag\">E<\/span> <span class=\"hljs-selector-tag\">--<\/span>&gt; <span class=\"hljs-selector-tag\">K<\/span>\n  <span class=\"hljs-selector-tag\">Q<\/span><span class=\"hljs-selector-attr\">&#91;Question]<\/span> <span class=\"hljs-selector-tag\">--<\/span>&gt; <span class=\"hljs-selector-tag\">R<\/span><span class=\"hljs-selector-attr\">&#91;Retrieval + graph traversal]<\/span>\n  <span class=\"hljs-selector-tag\">K<\/span> <span class=\"hljs-selector-tag\">--<\/span>&gt; <span class=\"hljs-selector-tag\">R<\/span>\n  <span class=\"hljs-selector-tag\">R<\/span> <span class=\"hljs-selector-tag\">--<\/span>&gt; <span class=\"hljs-selector-tag\">C<\/span><span class=\"hljs-selector-attr\">&#91;Authorize and assemble evidence]<\/span>\n  <span class=\"hljs-selector-tag\">C<\/span> <span class=\"hljs-selector-tag\">--<\/span>&gt; <span class=\"hljs-selector-tag\">L<\/span><span class=\"hljs-selector-attr\">&#91;LLM synthesis]<\/span>\n  <span class=\"hljs-selector-tag\">L<\/span> <span class=\"hljs-selector-tag\">--<\/span>&gt; <span class=\"hljs-selector-tag\">V<\/span><span class=\"hljs-selector-attr\">&#91;Citation and grounding checks]<\/span>\n  <span class=\"hljs-selector-tag\">V<\/span> <span class=\"hljs-selector-tag\">--<\/span>&gt; <span class=\"hljs-selector-tag\">A<\/span><span class=\"hljs-selector-attr\">&#91;Answer]<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">The graph can include both canonical entities and a lexical subgraph of documents\/chunks. An embedding index can live on graph nodes, while full-text search and structural Cypher can retrieve alternative candidates. This architecture is not an automatic feature of installing Neo4j; each stage needs design and evaluation.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">1.7 Benefits and trade-offs<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">GraphRAG can provide interpretable relationship paths, combine unstructured evidence with structured topology, support ownership and impact queries, and improve the evidence surface for multi-hop questions. It can also fail badly when extraction invents edges, names are merged incorrectly, documents are stale, or graph fan-out overwhelms the prompt. Security complexity increases when access rules differ between a source document and a globally shared entity.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Cost components include source parsing\/OCR where applicable, entity extraction LLM calls, entity-resolution review, embedding computation, graph storage, index building, retriever queries, reranking, generation, synchronization and on-call support. None of these costs imply GraphRAG is automatically more expensive or more accurate for a given workload: measure costs and benefits against baselines.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">1.8 Technology selection checklist<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Choose graph-enhanced retrieval only if the workload has an explicit graph-worthy need. Ask:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Do questions require entity relationships, paths, attribution or temporal joins?<\/li>\n\n\n\n<li>Are entities and relationships observable or reliably extractable?<\/li>\n\n\n\n<li>Can the organization maintain a schema, resolution policy and source lineage?<\/li>\n\n\n\n<li>Is the expected improvement over lexical\/vector retrieval measurable?<\/li>\n\n\n\n<li>Can graph traversal be bounded by depth, authorization, and latency budgets?<\/li>\n\n\n\n<li>Can the final answer expose evidence rather than just a plausible narrative?<\/li>\n<\/ol>\n\n\n\n<p class=\"wp-block-paragraph\">If the answer to the first question is no, start with a simpler hybrid-search RAG baseline.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">1.9 Mistakes, security and production guidance<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">The most damaging anti-pattern is treating a model-extracted edge as an unquestioned fact. Make source attribution mandatory; record confidence and version separately; do not let confidence substitute for validation. Another anti-pattern is performing graph traversal before authorization: a model can leak existence and structure of documents the user should not know about. A third is using unbounded <code>*<\/code> traversals or unqualified, many-to-many expansions for user requests.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For production, distinguish &#8220;model suggests an impact path&#8221; from &#8220;observability confirms an outage&#8221;. A graph may be stale, and dependencies are not the same as outage propagation.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">1.10 Chapter summary and review exercises<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">GraphRAG adds graph-aware evidence retrieval and composition to RAG. Its value is highest when relations are essential, but performance and quality must be evaluated, not assumed.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Exercises:<\/strong> (a) Give three questions that vector search alone should handle; (b) give three that require joins or multi-hop relations; (c) design a three-line measurement plan comparing lexical, vector and graph retrieval; (d) list four ways a faulty knowledge graph can produce a confidently wrong answer.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Primary references:<\/strong> <a href=\"https:\/\/neo4j.com\/docs\/neo4j-graphrag-python\/current\/\">Neo4j GraphRAG Python package<\/a>, <a href=\"https:\/\/neo4j.com\/docs\/neo4j-graphrag-python\/current\/user_guide_rag.html\">RAG user guide<\/a>, <a href=\"https:\/\/neo4j.com\/developer\/genai-ecosystem\/graphrag-python\/\">GraphRAG developer guide<\/a>.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Chapter 2 &#8211; RAG Fundamentals<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">2.1 Learning objectives<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">You should be able to diagram the RAG pipeline, choose a chunking strategy, explain embeddings and similarity search, identify ranking failure modes, and distinguish unsupported generation from insufficient retrieval.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">2.2 Why this matters<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">GraphRAG inherits nearly every problem of conventional RAG. If a loader drops tables, a chunker separates a definition from its condition, or a retriever finds the wrong policy version, adding a knowledge graph is not a reliable cure. A successful implementation first establishes an evidence retrieval baseline and a dataset for regression tests.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">2.3 Tokens and context windows<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">A token is a unit used by a model&#8217;s tokenizer; it is not necessarily a word or a character. Each model has input\/output limits and provider-specific accounting. The context window must accommodate system policy, question, conversation context if needed, evidence, and output budget. The amount of <em>available<\/em> context does not guarantee the model attends correctly to all included facts.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">A useful bookkeeping identity is:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">available_evidence_tokens = input_limit\n                            - system_and_policy_tokens\n                            - question_and_history_tokens\n                            - reserved_response_overhead<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Actual APIs differ in how they count reasoning and output tokens. Treat this formula as a planning aid, not a substitute for the selected provider&#8217;s current API limits.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">2.4 Retrieval pipeline, step by step<\/h3>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>Acquire:<\/strong> ingest authorized sources with stable IDs, timestamps and owner metadata.<\/li>\n\n\n\n<li><strong>Parse:<\/strong> extract text while preserving headings, tables, code blocks and source positions.<\/li>\n\n\n\n<li><strong>Normalize:<\/strong> clean encodings and boilerplate without discarding meaningful semantics.<\/li>\n\n\n\n<li><strong>Chunk:<\/strong> divide documents into retrievable units with limited overlap.<\/li>\n\n\n\n<li><strong>Embed \/ index:<\/strong> compute vector representations and\/or populate a lexical index.<\/li>\n\n\n\n<li><strong>Analyze query:<\/strong> identify intent, IDs, quoted terms, requested time, and principal scope.<\/li>\n\n\n\n<li><strong>Retrieve:<\/strong> generate a candidate set using lexical, vector or graph search.<\/li>\n\n\n\n<li><strong>Filter and rank:<\/strong> enforce access, remove duplicates, rerank and cap the evidence budget.<\/li>\n\n\n\n<li><strong>Generate:<\/strong> instruct the LLM to use evidence and abstain when insufficient.<\/li>\n\n\n\n<li><strong>Verify and evaluate:<\/strong> confirm citations and test against human-labeled reference answers.<\/li>\n<\/ol>\n\n\n\n<h3 class=\"wp-block-heading\">2.5 Chunking trade-offs<\/h3>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>Strategy<\/th><th>Advantages<\/th><th>Risks<\/th><th>Best use<\/th><\/tr><\/thead><tbody><tr><td>Fixed token\/character<\/td><td>Simple and predictable<\/td><td>Can split definitions, code and tables<\/td><td>Early baseline<\/td><\/tr><tr><td>Heading\/section aware<\/td><td>Preserves semantics<\/td><td>Unbalanced sections<\/td><td>Policies and manuals<\/td><\/tr><tr><td>Sentence\/paragraph<\/td><td>Natural readability<\/td><td>Cross-paragraph facts split<\/td><td>Articles and FAQs<\/td><\/tr><tr><td>Code-aware<\/td><td>Keeps symbols\/functions together<\/td><td>Parser\/tooling cost<\/td><td>Repositories<\/td><\/tr><tr><td>Hierarchical parent-child<\/td><td>Small retrieval chunks, larger context parents<\/td><td>Requires linking and more assembly logic<\/td><td>Long manuals, contracts<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Track each chunk&#8217;s <code>document_id<\/code>, <code>document_version<\/code>, <code>span_start<\/code>, <code>span_end<\/code>, <code>chunking_strategy_version<\/code> and permission scope. Without these, it becomes difficult to explain or delete an answer&#8217;s evidence.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">2.6 Embeddings and vector similarity<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">An embedding is a numeric representation intended to place semantically related inputs near each other in a vector space. The dimensionality is determined by the embedding model and configuration. Two models with the same dimension are not necessarily interchangeable.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Cosine similarity is typically written as:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">cosine(a, b) = dot(a, b) \/ (norm(a) * norm(b))<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">A vector index usually performs approximate nearest-neighbor (ANN) retrieval, so it may not recover the mathematically exact top-k vectors in every case. Neo4j documentation explicitly describes vector index retrieval as approximate. <a href=\"https:\/\/neo4j.com\/docs\/cypher-manual\/current\/indexes\/search-indexes\/vector-indexes\/\">Vector index manual<\/a>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">A strong test separates embedding drift, ANN candidate recall, reranker quality and end-answer correctness. These are different metrics and need different diagnosis.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">2.7 Lexical, semantic and hybrid retrieval<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">A lexical engine is valuable for strings such as <code>HTTP 503<\/code>, <code>INC-001<\/code>, <code>KEDA<\/code>, configuration keys, and vendor-specific terminology. A semantic index is valuable for paraphrases such as &#8220;why can customers not sign in&#8221; versus &#8220;authentication token validation failure.&#8221; Hybrid retrieval combines them; raw scores from Lucene and vector search should not be treated as equivalent without normalization or rank fusion. Neo4j provides full-text and vector indexes, with version-specific query syntax described in its Cypher manual.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Example expected intent routing:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>User question<\/th><th>Primary candidate search<\/th><th>Why<\/th><\/tr><\/thead><tbody><tr><td>What does <code>INC-001<\/code> say?<\/td><td>Lexical \/ exact ID<\/td><td>Exact record identifier<\/td><\/tr><tr><td>How do we recover login access?<\/td><td>Hybrid text and vector<\/td><td>Semantic description and runbook terminology<\/td><\/tr><tr><td>Which callers transitively use auth?<\/td><td>Graph traversal<\/td><td>Explicit dependency edges<\/td><\/tr><tr><td>Who approved the current password policy?<\/td><td>Graph + source metadata<\/td><td>Provenance and ownership<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\">2.8 Minimal pipeline pseudocode<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">The following is <strong>pseudocode<\/strong>, intentionally not a library API:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-comment\"># PSEUDOCODE - not an executable import of neo4j-graphrag<\/span>\n\ndef answer(question, principal):\n    intent = classify_intent(question)\n    candidates = retrieve_lexical_and_vector(question, intent)\n    candidates = authorize(candidates, principal)\n    candidates = rerank_and_deduplicate(question, candidates)\n    evidence = pack_within_token_budget(candidates)\n    <span class=\"hljs-keyword\">if<\/span> not has_sufficient_support(evidence):\n        <span class=\"hljs-keyword\">return<\/span> abstain_with_reason()\n    draft = llm_generate(question=question, evidence=evidence)\n    <span class=\"hljs-keyword\">return<\/span> verify_citations_and_return(draft, evidence)<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">The order is conceptual. Real systems may apply database-side filters <strong>before<\/strong> ANN retrieval and recheck authorization later; it is unsafe to rely only on post-retrieval filtering where unauthorized material may influence retrieval, caches, summaries or model inputs.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">2.9 Evaluation and debugging fundamentals<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Suppose a golden question has three relevant chunks, and the retriever&#8217;s top five contains two of them. Recall@5 is <code>2\/3<\/code>. Precision@5 is <code>2\/5<\/code>, provided all five returned chunks are considered and judged relevant\/nonrelevant. Retrieval recall and answer faithfulness are not interchangeable: a model can hallucinate despite correct retrieval, and it can answer correctly from incomplete evidence by coincidence.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Suggested metrics for the later evaluator:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Recall@k:<\/strong> fraction of known relevant evidence found in top-k.<\/li>\n\n\n\n<li><strong>MRR (mean reciprocal rank):<\/strong> rewards high placement of the first relevant result.<\/li>\n\n\n\n<li><strong>nDCG (normalized discounted cumulative gain):<\/strong> graded ranking quality, position-aware.<\/li>\n\n\n\n<li><strong>Evidence faithfulness:<\/strong> material answer claims supported by the retrieved authorized context.<\/li>\n\n\n\n<li><strong>Citation precision:<\/strong> cited spans genuinely support the corresponding statements.<\/li>\n\n\n\n<li><strong>p50\/p95 latency:<\/strong> tail and typical response times by pipeline stage.<\/li>\n\n\n\n<li><strong>Cost\/query:<\/strong> embedding, retrieval, reranking and generation components separately.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Do not invent numeric benchmarks. Set targets using representative workloads and compare on the same fixed evaluation set.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">2.10 Failure matrix<\/h3>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>Symptom<\/th><th>Likely issue<\/th><th>First diagnostic action<\/th><\/tr><\/thead><tbody><tr><td>Wrong policy revision quoted<\/td><td>Stale ingestion or no version filter<\/td><td>Inspect source version and index timestamp<\/td><\/tr><tr><td>Exact incident ID missed<\/td><td>Vector-only retrieval<\/td><td>Add exact or lexical candidate path<\/td><\/tr><tr><td>Plausible unsupported answer<\/td><td>Prompt not grounded \/ poor context<\/td><td>Map each claim to a source span<\/td><\/tr><tr><td>Related sentence not found<\/td><td>Chunk split or parser dropped text<\/td><td>Examine normalized chunks and offsets<\/td><\/tr><tr><td>Relevant document exists but missing<\/td><td>ACL issue or candidate recall<\/td><td>Compare authorized candidate sets in debug mode<\/td><\/tr><tr><td>Slow retrieval at high k<\/td><td>Index, fan-out, reranking or network<\/td><td>Trace stages and PROFILE appropriate Cypher<\/td><\/tr><tr><td>Results change after model swap<\/td><td>Embedding model drift<\/td><td>Re-embed and confirm index metadata<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\">2.11 Security and production considerations<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Documents are untrusted content and may contain prompt injection, malicious instructions, secrets, or irrelevant third-party links. Retrieved content cannot override application\/system policy. Keep API keys out of source and logs; minimize personally identifiable information in prompts; respect source ownership and retention rules. Citation formatting is useful but does not itself provide authorization or factual verification.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">2.12 Review exercises and references<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Exercises:<\/strong> Design a chunk schema for a runbook containing tables and commands. Write five golden retrieval questions, each with an expected evidence ID. Explain why swapping an embedding model without reindexing can invalidate retrieval. Describe an abstention policy when only one of three required sources is available.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Primary references:<\/strong> <a href=\"https:\/\/neo4j.com\/docs\/neo4j-graphrag-python\/current\/user_guide_rag.html\">GraphRAG RAG Guide<\/a>, <a href=\"https:\/\/neo4j.com\/docs\/cypher-manual\/current\/indexes\/search-indexes\/full-text-indexes\/\">Neo4j full-text indexes<\/a>, <a href=\"https:\/\/neo4j.com\/docs\/cypher-manual\/current\/indexes\/search-indexes\/vector-indexes\/\">Neo4j vector indexes<\/a>.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Chapter 3 &#8211; Graph Theory and Knowledge Graphs<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">3.1 Learning objectives<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Learn nodes, directed relationships, properties, paths, neighborhoods and graph schemas. Model a dependency problem in both graph and relational terms; choose an entity-resolution strategy; and design provenance, time and conflict handling without promoting model output directly to fact.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">3.2 Why graphs are useful<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Many factual questions concern relationships rather than isolated items: &#8220;Which services call the authentication API?&#8221;, &#8220;Which runbook mitigates this incident?&#8221;, &#8220;Which source supports the claim about an outage?&#8221; In a relational database these are joins, which can work very well. A graph makes the relationship traversals first-class and often more readable, but the correct data model is more important than the storage paradigm.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">3.3 Property-graph elements<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">A <strong>node<\/strong> represents a thing, event or assertion. A node has labels and key-value properties. A <strong>relationship<\/strong> connects a source node to a target node, has a type, and can have properties. A <strong>path<\/strong> is an ordered chain of nodes and relationships. A <strong>degree<\/strong> counts incident relationships; a high-degree hub can cause explosion in broad traversals. A <strong>neighborhood<\/strong> is the set of nodes reachable within a chosen number of hops.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Consider:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\">(<span class=\"hljs-selector-pseudo\">:Service<\/span> {<span class=\"hljs-attribute\">id<\/span>: <span class=\"hljs-string\">'checkout'<\/span>, name: <span class=\"hljs-string\">'Checkout'<\/span>})\n  <span class=\"hljs-selector-tag\">-<\/span><span class=\"hljs-selector-attr\">&#91;:DEPENDS_ON {kind: <span class=\"hljs-string\">'synchronous'<\/span>}]<\/span><span class=\"hljs-selector-tag\">-<\/span>&gt;\n(<span class=\"hljs-selector-pseudo\">:Service<\/span> {<span class=\"hljs-attribute\">id<\/span>: <span class=\"hljs-string\">'payments'<\/span>, name: <span class=\"hljs-string\">'Payments'<\/span>})<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-4\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">This means Checkout depends on Payments. The arrow encodes <strong>directional semantics<\/strong>. You may query against the arrow to find direct dependencies, or reverse direction to find upstream consumers, but those are different questions. Relationships are directed in Neo4j. Cypher can match without specifying arrow direction when appropriate; that does not mean the stored relationship is intrinsically undirected.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">3.4 A semantic data model<\/h3>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">flowchart LR\n  T&#91;Team] --&gt;|OWNS| S&#91;Service]\n  S --&gt;|DEPENDS_ON| D&#91;Service]\n  I&#91;Incident] --&gt;|AFFECTED| D\n  R&#91;Runbook] --&gt;|MITIGATES| I\n  Doc&#91;Document] --&gt;|HAS_CHUNK| C&#91;Chunk]\n  C --&gt;|MENTIONS| D\n  C --&gt;|SUPPORTS| F&#91;Fact]\n  F --&gt;|ABOUT| D<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\"><code>Service<\/code>, <code>Team<\/code>, <code>Incident<\/code>, <code>Runbook<\/code>, <code>Document<\/code>, <code>Chunk<\/code>, and <code>Fact<\/code> are labels\/roles in the reference schema. A graph schema documents legal node\/edge types, stable identifiers, required fields, cardinality expectations and source rules. Not every convention can be enforced by a database constraint. Application validation remains necessary, especially in Community Edition.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">3.5 Paths and graph traversal<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">A dependency query may ask for services reachable within one to three hops. The following <strong>illustrative Cypher<\/strong> returns unique names of upstream consumers of Payments; Lab 01 contains the exact executable query with parameters and tenant checks.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-5\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-tag\">MATCH<\/span> (<span class=\"hljs-selector-tag\">consumer<\/span><span class=\"hljs-selector-pseudo\">:Service)-<\/span><span class=\"hljs-selector-attr\">&#91;:DEPENDS_ON*1..3]<\/span><span class=\"hljs-selector-tag\">-<\/span>&gt;(<span class=\"hljs-selector-tag\">target<\/span><span class=\"hljs-selector-pseudo\">:Service<\/span> {<span class=\"hljs-attribute\">id<\/span>: <span class=\"hljs-string\">'payments'<\/span>})\n<span class=\"hljs-selector-tag\">RETURN<\/span> <span class=\"hljs-selector-tag\">DISTINCT<\/span> <span class=\"hljs-selector-tag\">consumer<\/span><span class=\"hljs-selector-class\">.name<\/span> <span class=\"hljs-selector-tag\">AS<\/span> <span class=\"hljs-selector-tag\">potentialConsumer<\/span>\n<span class=\"hljs-selector-tag\">ORDER<\/span> <span class=\"hljs-selector-tag\">BY<\/span> <span class=\"hljs-selector-tag\">potentialConsumer<\/span>;<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-5\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Paths are not causality proofs. A service can depend on a dependency for a noncritical feature, use graceful degradation or route around a failure. Include relationship type, criticality, temporal validity and operational telemetry when forming live impact conclusions.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Unbounded variable-length patterns like <code>[:DEPENDS_ON*]<\/code> can be unexpectedly expensive or return irrelevant distant neighbors. The baseline sets an explicit maximum of three hops, then measures whether that is appropriate. Use <code>PROFILE<\/code>, known cardinalities, query timeouts and access controls for production.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">3.6 Property graphs vs RDF and ontologies<\/h3>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>Dimension<\/th><th>Labeled property graph (Neo4j)<\/th><th>RDF \/ OWL ecosystem<\/th><\/tr><\/thead><tbody><tr><td>Basic structure<\/td><td>Nodes and typed relationships with properties<\/td><td>Subject-predicate-object statements (triples)<\/td><\/tr><tr><td>Query language<\/td><td>Cypher<\/td><td>SPARQL (commonly)<\/td><\/tr><tr><td>Schema<\/td><td>Labels, constraints, app rules and graph conventions<\/td><td>RDF Schema and ontology languages such as OWL\/SHACL<\/td><\/tr><tr><td>Strength<\/td><td>Application traversal and graph pattern retrieval<\/td><td>Semantic interoperability and formal vocabularies\/inference<\/td><\/tr><tr><td>Risks<\/td><td>Under-specified meanings or inconsistent labels<\/td><td>Ontology\/inference and modeling complexity<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">These are broad modeling tendencies rather than a claim that one approach is objectively superior. Ontology-style discipline is useful in property-graph systems too: define relationship meaning, allowed endpoints, cardinalities and validation rules.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">3.7 Entity resolution and canonical IDs<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Unstructured sources might refer to the same system as &#8220;auth&#8221;, &#8220;identity-service&#8221;, and &#8220;Login API&#8221;. Blindly merging them can poison an entire graph. A safe resolution procedure is:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Extract the mention and source span.<\/li>\n\n\n\n<li>Generate candidate canonical entities using exact names, aliases and optional semantic similarity.<\/li>\n\n\n\n<li>Compare identifiers, environment, organization scope, time and expected entity type.<\/li>\n\n\n\n<li>Accept high-certainty matches under explicit rules, put uncertain ones in review, and keep unresolved mentions separate.<\/li>\n\n\n\n<li>Preserve the alias, source and resolution decision, rather than losing all evidence of what the original document said.<\/li>\n<\/ol>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Do not use LLM-generated display names as universal identities.<\/strong> In a multi-tenant environment, canonical identity is commonly scoped by <code>(tenant_id, external_source, external_id)<\/code> or a trustworthy application-managed UUID. An equally named service in two organizations might not be the same entity.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">3.8 Facts, provenance and conflicting information<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">A graph edge such as <code>(:Service)-[:DEPENDS_ON]-&gt;(:Service)<\/code> is convenient for traversal, but can obscure where the information came from. For a changeable or disputed relationship, model an explicit assertion:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-6\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\">Fact F<span class=\"hljs-number\">-77<\/span>:\n  subject: checkout\n  <span class=\"hljs-attr\">predicate<\/span>: DEPENDS_ON\n  <span class=\"hljs-attr\">object<\/span>: payments\n  <span class=\"hljs-attr\">source_document<\/span>: architecture-v4\n  <span class=\"hljs-attr\">source_span<\/span>: section<span class=\"hljs-number\">-3.2<\/span>\n  <span class=\"hljs-attr\">observed_at<\/span>: <span class=\"hljs-number\">2026<\/span><span class=\"hljs-number\">-10<\/span><span class=\"hljs-number\">-01<\/span>\n  <span class=\"hljs-attr\">valid_from<\/span>: <span class=\"hljs-number\">2026<\/span><span class=\"hljs-number\">-09<\/span><span class=\"hljs-number\">-01<\/span>\n  <span class=\"hljs-attr\">valid_to<\/span>: <span class=\"hljs-literal\">null<\/span>\n  <span class=\"hljs-attr\">verification_status<\/span>: reviewed<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-6\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">A <code>Fact<\/code> node can connect to source chunk and resolved entities. A separate materialized <code>DEPENDS_ON<\/code> edge can accelerate operations, but must be rebuildable from reviewed facts. Distinguish original source claim, reviewer decision, derived materialized edge and query-time answer. Conflicting sources may both be worth storing; don&#8217;t silently overwrite one with another.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Time has three meanings:<\/strong> <code>event_time<\/code> (when an incident happened), <code>valid_time<\/code> (when a fact was true), and <code>ingested_at<\/code> (when data entered the graph). Failure to model the difference can cause historical questions to use present-day relationships.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">3.9 Graph schema contracts and validation<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">For Northstar Labs, an initial schema contract could be:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>Relationship<\/th><th>Allowed source<\/th><th>Allowed target<\/th><th>Cardinality expectation<\/th><th>Evidence requirement<\/th><\/tr><\/thead><tbody><tr><td><code>DEPENDS_ON<\/code><\/td><td>Service<\/td><td>Service<\/td><td>0..many<\/td><td>Inventory source or reviewed change<\/td><\/tr><tr><td><code>OWNS<\/code><\/td><td>Team<\/td><td>Service<\/td><td>0..many<\/td><td>Owner\/team registry<\/td><\/tr><tr><td><code>AFFECTED<\/code><\/td><td>Incident<\/td><td>Service<\/td><td>1..many<\/td><td>Incident ticket \/ telemetry<\/td><\/tr><tr><td><code>MITIGATES<\/code><\/td><td>Runbook<\/td><td>Incident<\/td><td>0..many<\/td><td>Reviewed linkage<\/td><\/tr><tr><td><code>HAS_CHUNK<\/code><\/td><td>Document<\/td><td>Chunk<\/td><td>1..many<\/td><td>Parser and chunk version<\/td><\/tr><tr><td><code>MENTIONS<\/code><\/td><td>Chunk<\/td><td>Entity<\/td><td>0..many<\/td><td>Mention span, extraction version<\/td><\/tr><tr><td><code>SUPPORTS<\/code><\/td><td>Chunk<\/td><td>Fact<\/td><td>0..many<\/td><td>Source span or recorded assertion<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">A validator should reject unknown labels, illegal relationship endpoint types, missing tenant scope where required, self-dependency except documented special cases, absent required provenance, and relationships inconsistent with canonical IDs. Do not assume Neo4j Community constraints automatically validate all these conditions.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">3.10 Common graph quality failures<\/h3>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>Failure<\/th><th>Consequence<\/th><th>Mitigation<\/th><\/tr><\/thead><tbody><tr><td>Duplicate entity nodes<\/td><td>Paths\/answers split<\/td><td>Stable canonical keys and resolution review<\/td><\/tr><tr><td>Incorrect alias merge<\/td><td>Cross-system relationships invented<\/td><td>Candidate thresholds and human review<\/td><\/tr><tr><td>Old dependency remains active<\/td><td>False impact<\/td><td>Valid-time fields and stale-data alerts<\/td><\/tr><tr><td>Edge without source<\/td><td>Unverifiable reasoning<\/td><td>Fact\/provenance contract<\/td><\/tr><tr><td>Very high-degree hub expansion<\/td><td>Latency and irrelevant context<\/td><td>Bounded traversal and relevance caps<\/td><\/tr><tr><td>Cross-tenant shared node<\/td><td>Access leak<\/td><td>Scoped identity, database isolation and query-policy checks<\/td><\/tr><tr><td>Conflicting documents overwritten<\/td><td>Loss of truth history<\/td><td>Versioned assertions and conflict markers<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\">3.11 Practical exercises<\/h3>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Draw a property graph for three services, one incident and two runbooks. Give arrows explicit meanings.<\/li>\n\n\n\n<li>Name two entity-resolution cases requiring human review, not auto-merge.<\/li>\n\n\n\n<li>Write a business rule distinguishing a direct dependency from a possible downstream impact.<\/li>\n\n\n\n<li>Design a historical query for &#8220;What depended on authentication on October 1?&#8221; and list the required timestamps.<\/li>\n\n\n\n<li>Decide what an answer must do when two equally authoritative documents disagree.<\/li>\n<\/ol>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Key takeaway:<\/strong> a knowledge graph must represent identity, semantics, provenance, time and access scope. Traversal correctness follows from graph correctness; a sophisticated LLM cannot reliably recover missing evidence.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Primary references:<\/strong> <a href=\"https:\/\/neo4j.com\/docs\/cypher-manual\/current\/\">Neo4j Cypher manual<\/a>, <a href=\"https:\/\/neo4j.com\/docs\/neo4j-graphrag-python\/current\/user_guide_kg_builder.html\">Knowledge Graph Builder<\/a>, <a href=\"https:\/\/neo4j.com\/docs\/cypher-manual\/current\/schema\/constraints\/create-constraints\/\">Neo4j constraint documentation<\/a>.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Chapter 4 &#8211; Neo4j Fundamentals<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">4.1 Learning objectives<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Run Neo4j locally, connect with the official Python driver, create a small graph safely, execute parameterized Cypher, inspect constraints and indexes, and explain what remains to be done before operating a production database.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">4.2 Neo4j architecture and editions<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Neo4j is a property-graph database. Users and applications issue Cypher through supported drivers. The database manages nodes, relationships, indexes, concurrency and transactions. The GraphRAG Python package is an application library layered <strong>on top<\/strong> of Neo4j and the Python driver, not a replacement for the database or an automatic ETL engine.<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>Deployment<\/th><th>Suitable for<\/th><th>Production consideration<\/th><\/tr><\/thead><tbody><tr><td>Community Edition \/ single local container<\/td><td>Training, proof of concept<\/td><td>Different HA, backup and security capabilities from Enterprise<\/td><\/tr><tr><td>Self-managed Enterprise<\/td><td>Organizations managing infrastructure<\/td><td>Licensing, clusters, operational upgrades and recovery procedures<\/td><\/tr><tr><td>Neo4j Aura managed services<\/td><td>Managed production or evaluation<\/td><td>Tier-specific limits and features; confirm SLA, region and capabilities<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Neo4j&#8217;s online backup\/restore tooling is edition-sensitive. For example, the official backup-restore manual identifies online backup restore functionality as Enterprise-specific. Do not interpret Docker volume persistence as a verified backup strategy. <a href=\"https:\/\/neo4j.com\/docs\/operations-manual\/current\/backup-restore\/restore-backup\/\">Backup\/restore manual<\/a>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">4.3 Version assumptions and prerequisites<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Python 3.13 (used by this edition).<\/li>\n\n\n\n<li>A Docker implementation capable of <code>docker compose<\/code> and sufficient memory for Neo4j.<\/li>\n\n\n\n<li>Neo4j Docker <code>2026.09.0<\/code> as the lab pin; this is documented by Neo4j. The research-time newest Neo4j release is 2026.10.0, but its Docker image was not independently checked here.<\/li>\n\n\n\n<li>Python driver version range <code>neo4j>=6.3.1,&lt;7<\/code>; local lab installation is covered in its README.<\/li>\n\n\n\n<li>Browser interface at <code>http:\/\/localhost:7474<\/code> and Bolt driver at <code>neo4j:\/\/localhost:7687<\/code> with localhost-only published ports.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">All values in <code>.env.example<\/code> are synthetic local examples, not acceptable production passwords.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">4.4 Lab 01 project layout<\/h3>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">labs\/01-foundations\/\n  .env.example\n  .gitignore\n  docker-compose.yml\n  requirements.txt\n  README.md\n  src\/\n    dataset.py\n    graph_queries.py\n    seed.py\n    explore.py\n  tests\/\n    test_dataset.py\n    test_cypher_contract.py<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Lab 01 deliberately has <strong>no LLM provider or embedding cost<\/strong>. It introduces durable graph mechanics first and becomes the seed dataset for later GraphRAG retrieval chapters.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">4.5 Start and initialize<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">From <code>labs\/01-foundations\/<\/code>:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-7\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\">cp .env.example .env\n<span class=\"hljs-comment\"># Edit .env to set a unique password; keep NEO4J_AUTH and NEO4J_PASSWORD consistent.<\/span>\ndocker compose up -d\npython3<span class=\"hljs-number\">.13<\/span> -m venv .venv\n. .venv\/bin\/activate\npython -m pip install -r requirements.txt\npython src\/seed.py\npython src\/explore.py payments\npython -m pytest -q<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-7\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">For macOS\/Linux, <code>. .venv\/bin\/activate<\/code> activates the virtual environment. For Windows PowerShell, use <code>.venv\\\\Scripts\\\\Activate.ps1<\/code> after creating the venv with a suitable <code>py -3.13<\/code> command. The Docker and Python dependencies must be installed first; commands are not claimed as executed in this environment.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Database is <strong>locally configured only<\/strong> in the example; the lab&#8217;s <code>.env<\/code> is ignored by Git. Do not run this unsecured configuration on a public host.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">4.6 Schema constraints and why they matter<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">The lab creates uniqueness constraints on stable IDs:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-8\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-tag\">CREATE<\/span> <span class=\"hljs-selector-tag\">CONSTRAINT<\/span> <span class=\"hljs-selector-tag\">service_id<\/span> <span class=\"hljs-selector-tag\">IF<\/span> <span class=\"hljs-selector-tag\">NOT<\/span> <span class=\"hljs-selector-tag\">EXISTS<\/span>\n<span class=\"hljs-selector-tag\">FOR<\/span> (<span class=\"hljs-selector-tag\">s<\/span><span class=\"hljs-selector-pseudo\">:Service)<\/span> <span class=\"hljs-selector-tag\">REQUIRE<\/span> <span class=\"hljs-selector-tag\">s<\/span><span class=\"hljs-selector-class\">.id<\/span> <span class=\"hljs-selector-tag\">IS<\/span> <span class=\"hljs-selector-tag\">UNIQUE<\/span>;\n\n<span class=\"hljs-selector-tag\">CREATE<\/span> <span class=\"hljs-selector-tag\">CONSTRAINT<\/span> <span class=\"hljs-selector-tag\">team_id<\/span> <span class=\"hljs-selector-tag\">IF<\/span> <span class=\"hljs-selector-tag\">NOT<\/span> <span class=\"hljs-selector-tag\">EXISTS<\/span>\n<span class=\"hljs-selector-tag\">FOR<\/span> (<span class=\"hljs-selector-tag\">t<\/span><span class=\"hljs-selector-pseudo\">:Team)<\/span> <span class=\"hljs-selector-tag\">REQUIRE<\/span> <span class=\"hljs-selector-tag\">t<\/span><span class=\"hljs-selector-class\">.id<\/span> <span class=\"hljs-selector-tag\">IS<\/span> <span class=\"hljs-selector-tag\">UNIQUE<\/span>;\n\n<span class=\"hljs-selector-tag\">CREATE<\/span> <span class=\"hljs-selector-tag\">CONSTRAINT<\/span> <span class=\"hljs-selector-tag\">incident_id<\/span> <span class=\"hljs-selector-tag\">IF<\/span> <span class=\"hljs-selector-tag\">NOT<\/span> <span class=\"hljs-selector-tag\">EXISTS<\/span>\n<span class=\"hljs-selector-tag\">FOR<\/span> (<span class=\"hljs-selector-tag\">i<\/span><span class=\"hljs-selector-pseudo\">:Incident)<\/span> <span class=\"hljs-selector-tag\">REQUIRE<\/span> <span class=\"hljs-selector-tag\">i<\/span><span class=\"hljs-selector-class\">.id<\/span> <span class=\"hljs-selector-tag\">IS<\/span> <span class=\"hljs-selector-tag\">UNIQUE<\/span>;\n\n<span class=\"hljs-selector-tag\">CREATE<\/span> <span class=\"hljs-selector-tag\">CONSTRAINT<\/span> <span class=\"hljs-selector-tag\">runbook_id<\/span> <span class=\"hljs-selector-tag\">IF<\/span> <span class=\"hljs-selector-tag\">NOT<\/span> <span class=\"hljs-selector-tag\">EXISTS<\/span>\n<span class=\"hljs-selector-tag\">FOR<\/span> (<span class=\"hljs-selector-tag\">r<\/span><span class=\"hljs-selector-pseudo\">:Runbook)<\/span> <span class=\"hljs-selector-tag\">REQUIRE<\/span> <span class=\"hljs-selector-tag\">r<\/span><span class=\"hljs-selector-class\">.id<\/span> <span class=\"hljs-selector-tag\">IS<\/span> <span class=\"hljs-selector-tag\">UNIQUE<\/span>;<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-8\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">In Community Edition, uniqueness constraints are available, but certain key and property-existence constraints are Enterprise-specific. Uniqueness by itself does not force every node to have an <code>id<\/code> property: the application must set it. A production multitenant design generally needs stronger tenant-specific schema, policy and possibly separate databases depending on risk. <a href=\"https:\/\/neo4j.com\/docs\/cypher-manual\/current\/schema\/constraints\/create-constraints\/\">Neo4j constraints<\/a>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">4.7 Idempotent upserts and relationships<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">This is the actual shape used by <code>src\/graph_queries.py<\/code>:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-9\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\">MERGE (s:Service {id: $id})\nSET s.name = $name,\n    s.tenant_id = $tenant_id,\n    s.environment = $environment;<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-9\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\"><code>MERGE<\/code> matches or creates the node with the supplied key, and <code>SET<\/code> updates its properties. It does not magically resolve collisions if different source systems reuse the same ID or if there were pre-existing duplicates. This lab has a single synthetic tenant and an application-owned stable ID namespace.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">A directed relationship uses:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-10\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-tag\">MATCH<\/span> (<span class=\"hljs-selector-tag\">consumer<\/span><span class=\"hljs-selector-pseudo\">:Service<\/span> {<span class=\"hljs-attribute\">id<\/span>: $consumer_id})\n<span class=\"hljs-selector-tag\">MATCH<\/span> (<span class=\"hljs-selector-tag\">provider<\/span><span class=\"hljs-selector-pseudo\">:Service<\/span> {<span class=\"hljs-attribute\">id<\/span>: $provider_id})\n<span class=\"hljs-selector-tag\">MERGE<\/span> (<span class=\"hljs-selector-tag\">consumer<\/span>)<span class=\"hljs-selector-tag\">-<\/span><span class=\"hljs-selector-attr\">&#91;:DEPENDS_ON]<\/span><span class=\"hljs-selector-tag\">-<\/span>&gt;(<span class=\"hljs-selector-tag\">provider<\/span>);<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-10\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">This <code>MATCH<\/code>-then-<code>MERGE<\/code> pattern avoids silently creating missing services during relationship ingestion. It still needs error detection when a referenced service is absent, which Lab 01 performs using dataset referential-integrity tests. Later chapters add reconciliation for external connectors and graph audit checks.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">4.8 Parameterized Cypher and impact analysis<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">The following is the lab&#8217;s core read query (whitespace may differ):<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-11\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\">MATCH path = (consumer:Service)-&#91;:DEPENDS_ON*<span class=\"hljs-number\">1.<\/span><span class=\"hljs-number\">.3<\/span>]-&gt;\n             (target:Service {id: $service_id})\nWHERE all(n IN nodes(path) WHERE n.tenant_id = $tenant_id)\n<span class=\"hljs-keyword\">RETURN<\/span> DISTINCT consumer.id <span class=\"hljs-keyword\">AS<\/span> id, consumer.name <span class=\"hljs-keyword\">AS<\/span> name\nORDER BY id;<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-11\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">This is deliberately <strong>bounded<\/strong> to three hops, <strong>parameterized<\/strong> by service ID, and checks the tenant label on <strong>all nodes in each matched path<\/strong>, not just the endpoints. It is a learning example, not a substitute for hard database tenant isolation or access control. For a user-facing service, authorization should include a trustworthy principal mapping, role\/scope constraints, and database policy.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For the synthetic dataset, the expected results when querying <code>payments<\/code> are:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>ID<\/th><th>Service name<\/th><th>Why it matches<\/th><\/tr><\/thead><tbody><tr><td><code>api-gateway<\/code><\/td><td>API Gateway<\/td><td>Gateway -&gt; Checkout -&gt; Payments<\/td><\/tr><tr><td><code>checkout<\/code><\/td><td>Checkout<\/td><td>Checkout -&gt; Payments<\/td><\/tr><tr><td><code>notification<\/code><\/td><td>Notification<\/td><td>Notification -&gt; Payments<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">The table shows <strong>expected graph results<\/strong>, not a live execution screenshot. Run the provided script to verify against your environment.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">4.9 Query planning and indexes<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Useful Cypher commands:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-12\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-tag\">SHOW<\/span> <span class=\"hljs-selector-tag\">CONSTRAINTS<\/span>;\n<span class=\"hljs-selector-tag\">SHOW<\/span> <span class=\"hljs-selector-tag\">INDEXES<\/span>;\n<span class=\"hljs-selector-tag\">EXPLAIN<\/span> <span class=\"hljs-selector-tag\">MATCH<\/span> (<span class=\"hljs-selector-tag\">s<\/span><span class=\"hljs-selector-pseudo\">:Service<\/span> {<span class=\"hljs-attribute\">id<\/span>: <span class=\"hljs-string\">'payments'<\/span>}) <span class=\"hljs-selector-tag\">RETURN<\/span> <span class=\"hljs-selector-tag\">s<\/span><span class=\"hljs-selector-class\">.name<\/span>;\n<span class=\"hljs-selector-tag\">PROFILE<\/span> <span class=\"hljs-selector-tag\">MATCH<\/span> (<span class=\"hljs-selector-tag\">s<\/span><span class=\"hljs-selector-pseudo\">:Service<\/span> {<span class=\"hljs-attribute\">id<\/span>: <span class=\"hljs-string\">'payments'<\/span>}) <span class=\"hljs-selector-tag\">RETURN<\/span> <span class=\"hljs-selector-tag\">s<\/span><span class=\"hljs-selector-class\">.name<\/span>;<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-12\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\"><code>EXPLAIN<\/code> obtains an execution plan without executing the query; <code>PROFILE<\/code> executes the query and provides plan metrics, so avoid <code>PROFILE<\/code> on expensive or write queries without understanding their effects. Constraints backed by indexes help initial node lookup, while traversal complexity still depends on degree, path depth and filters.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For later semantic retrieval, Neo4j supports vector indexes and full-text indexes:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-13\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-comment\">\/\/ Later chapter only: run AFTER adding 1536-dimensional embeddings.<\/span>\nCREATE VECTOR INDEX chunkEmbedding IF NOT EXISTS\nFOR (c:Chunk) ON c.embedding\nOPTIONS {<span class=\"hljs-attr\">indexConfig<\/span>: {\n  <span class=\"hljs-string\">`vector.dimensions`<\/span>: <span class=\"hljs-number\">1536<\/span>,\n  <span class=\"hljs-string\">`vector.similarity_function`<\/span>: <span class=\"hljs-string\">'cosine'<\/span>\n}};\n\nCREATE FULLTEXT INDEX chunkText IF NOT EXISTS\nFOR (c:Chunk) ON EACH &#91;c.text];\n\nSHOW VECTOR INDEXES;\nSHOW FULLTEXT INDEXES;<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-13\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\"><strong>Important:<\/strong> These commands are illustrative previews, not required for Lab 01, which does not yet have <code>Chunk<\/code> nodes or embeddings. A newly created index may initially be <code>POPULATING<\/code>; wait for <code>ONLINE<\/code>. The 1536-dimensional index is a configuration example, not a declaration that every embedding model uses 1536 dimensions. <a href=\"https:\/\/neo4j.com\/docs\/cypher-manual\/current\/indexes\/search-indexes\/vector-indexes\/\">Neo4j vector indexes<\/a>, <a href=\"https:\/\/neo4j.com\/docs\/cypher-manual\/current\/indexes\/search-indexes\/full-text-indexes\/\">full-text indexes<\/a>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">4.10 Python driver lifecycle and error handling<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">The scripts use the official driver context manager, <code>verify_connectivity<\/code>, <code>Driver.execute_query<\/code> with <code>database_=<\/code> and <code>parameters_=<\/code>. The Neo4j driver automatically retries some transient failures on <code>execute_query<\/code>; non-transient errors still require handling, and ingestion must be designed for partial progress. The driver should be reused across operations rather than recreated per query. <a href=\"https:\/\/neo4j.com\/docs\/python-manual\/current\/query-simple\/\">Neo4j driver manual<\/a>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">A lab-level execution cycle is:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-14\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-keyword\">from<\/span> neo4j <span class=\"hljs-keyword\">import<\/span> GraphDatabase\n\n<span class=\"hljs-keyword\">with<\/span> GraphDatabase.driver(uri, auth=(user, password)) <span class=\"hljs-keyword\">as<\/span> driver:\n    driver.verify_connectivity()\n    records, summary, keys = driver.execute_query(\n        <span class=\"hljs-string\">\"MATCH (s:Service {id: $id}) RETURN s.name AS name\"<\/span>,\n        parameters_={<span class=\"hljs-string\">\"id\"<\/span>: <span class=\"hljs-string\">\"payments\"<\/span>},\n        database_=<span class=\"hljs-string\">\"neo4j\"<\/span>,\n    )\n    <span class=\"hljs-keyword\">for<\/span> record <span class=\"hljs-keyword\">in<\/span> records:\n        print(record&#91;<span class=\"hljs-string\">\"name\"<\/span>])<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-14\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">The script is <strong>API-documentation-based<\/strong>, but has not been runtime-tested with a Neo4j server during this edition&#8217;s generation. The <code>GraphDatabase.driver<\/code> context-manager pattern, parameterized <code>execute_query<\/code>, and explicit database selection are supported in the official Python driver manual.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">4.11 Common mistakes and troubleshooting<\/h3>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>Symptom<\/th><th>Cause to check<\/th><th>Action<\/th><\/tr><\/thead><tbody><tr><td>Connection refused on 7687<\/td><td>Container not ready or wrong port<\/td><td><code>docker compose ps<\/code>, then <code>docker compose logs neo4j<\/code><\/td><\/tr><tr><td>Authentication failed<\/td><td><code>NEO4J_AUTH<\/code> differs from client secret or persisted database already has another password<\/td><td>Check local <code>.env<\/code>; for a throwaway lab only, recreate volumes after backing up anything useful<\/td><\/tr><tr><td>Query yields zero results<\/td><td>Seed absent, wrong DB or service ID<\/td><td>Run seed and query known IDs<\/td><\/tr><tr><td>Duplicate nodes<\/td><td>Missing stable keys \/ constraints<\/td><td>Inspect constraints and apply migration before re-ingestion<\/td><\/tr><tr><td>Traversal is slow<\/td><td>Fanout or missing bound<\/td><td>Tighten depth; use <code>PROFILE<\/code> and observe cardinality<\/td><\/tr><tr><td><code>SEARCH<\/code> syntax fails on old server<\/td><td>Version or Cypher language mode incompatible<\/td><td>Verify Neo4j version and required Cypher 25 syntax<\/td><\/tr><tr><td>Vector search finds nothing<\/td><td>Empty, wrong-dimension or populating index<\/td><td>Check embedding model\/dimensions and <code>SHOW VECTOR INDEXES<\/code><\/td><\/tr><tr><td>Password change seems ignored<\/td><td>Existing persisted <code>\/data<\/code> volume keeps old auth state<\/td><td>Follow documented password recovery; do not assume env reset overwrites DB<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\">4.12 Backup, security, and operations<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">The local volume is for persistence, not a backup. A production plan must define RPO (recovery point objective), RTO (recovery time objective), tested restore, TLS, private networking, principal access control, read-only retrieval users, secrets management, logs and upgrade rehearsals. Avoid exposing Bolt and Browser interfaces to the public internet. Application roles may need strict database privileges, and edition-specific capabilities must be confirmed for the selected license or Aura tier.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For experiments only, <code>docker compose down<\/code> stops and removes the lab container while preserving its named volume by default. <code>docker compose down -v<\/code> additionally destroys the named lab volume; use it only when the lab data is disposable.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">4.13 Exercises, outputs and review<\/h3>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Start the lab, ingest the synthetic dataset twice, and confirm node counts do not increase on the second run.<\/li>\n\n\n\n<li>Query the directly dependent services of <code>auth<\/code> and compare with the transitive result.<\/li>\n\n\n\n<li>Show the shortest known path from <code>api-gateway<\/code> to <code>payments<\/code> within the modeled graph.<\/li>\n\n\n\n<li>Run <code>EXPLAIN<\/code> for a unique-ID lookup and identify the index-backed starting point.<\/li>\n\n\n\n<li>Deliberately change the service ID query parameter and observe the empty result instead of string interpolation.<\/li>\n\n\n\n<li>Extend the dataset with one service and a new dependency; predict impact before running the query.<\/li>\n<\/ol>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Expected verification:<\/strong> the lab seeds six services, three teams, two incidents and two runbooks, and prints three potential upstream consumers of <code>payments<\/code>; the reference test suite validates dataset integrity and Cypher query contracts offline. Live execution remains a user-environment step until a Neo4j runtime is available.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Primary references:<\/strong> <a href=\"https:\/\/neo4j.com\/docs\/operations-manual\/current\/docker\/introduction\/\">Neo4j Docker guide<\/a>, <a href=\"https:\/\/neo4j.com\/docs\/python-manual\/current\/\">Python driver<\/a>, <a href=\"https:\/\/neo4j.com\/docs\/cypher-manual\/current\/\">Cypher manual<\/a>, <a href=\"https:\/\/neo4j.com\/docs\/cypher-manual\/current\/schema\/constraints\/create-constraints\/\">constraints<\/a>, <a href=\"https:\/\/neo4j.com\/docs\/cypher-manual\/current\/indexes\/search-indexes\/vector-indexes\/\">vector indexes<\/a>, <a href=\"https:\/\/neo4j.com\/docs\/operations-manual\/current\/backup-restore\/restore-backup\/\">backup and restore<\/a>.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Part I closure report<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Chapters authored:<\/strong> 1. Introduction to GraphRAG; 2. RAG Fundamentals; 3. Graph Theory and Knowledge Graphs; 4. Neo4j Fundamentals.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Technical decisions:<\/strong> synthetic Northstar Labs example corpus; stable IDs; directional dependency semantics; tenant-aware bounded traversal as a learning baseline; source provenance and historical validity as first-class concerns; Python 3.13, Neo4j GraphRAG package 1.22.0 when introduced, documented Docker image 2026.09.0 for Lab 01, and Neo4j 2026.10.0 as research-time current server release.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Files introduced:<\/strong> <code>PART_I_FOUNDATIONS.md<\/code>, Lab 01 source\/data\/Compose\/test files, source and verification ledgers.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Outstanding:<\/strong> Live Neo4j Docker execution, exact environment dependency lock hashes, verified retrieval+generation code, later embedding index migration, access policy and end-to-end benchmark suite.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Exact next chapter:<\/strong> Chapter 5 &#8211; Neo4j GraphRAG Architecture (Part II).<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Github Ref &#8211; https:\/\/github.com\/AiOpsSchool\/neo4j-graphrag-master-guide Chapters 1-4 | Neo4j GraphRAG Master Guide | Research date: 2026-10-21 Scope: This is the authored foundations volume. It deliberately does not claim&#8230; <\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_joinchat":[],"footnotes":""},"categories":[11138],"tags":[],"class_list":["post-79584","post","type-post","status-publish","format-standard","hentry","category-best-tools"],"_links":{"self":[{"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/posts\/79584","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/comments?post=79584"}],"version-history":[{"count":1,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/posts\/79584\/revisions"}],"predecessor-version":[{"id":79585,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/posts\/79584\/revisions\/79585"}],"wp:attachment":[{"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/media?parent=79584"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/categories?post=79584"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/tags?post=79584"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}