Cypher Query Tutorial: MATCH, CREATE, MERGE, and Graph Traversal for GraphRAG Applications

TL;DR — Cypher query tutorial: MATCH, CREATE, MERGE, traversal, vector search. Neo4j Cypher Manual: "Cypher is Neo4j declarative query language for expressive and efficient querying of property graphs." Core Concepts: "Quantified relationships find paths up to N hops. DISTINCT for unique results. Fixed {n}, range 1..3, any ." Basic Queries: "Fixed length with quantifier {n}, variable length with *1..3, shortest path with shortestPath()." GraphRAG Python: "HybridCypherRetriever: vector + fulltext index, retrieval query traverses graph for more context." Cypher Decoded: "MATCH, WHERE, RETURN, CREATE, MERGE, SET — core knowledge for Neo4j queries." Learn more with knowledge graph basics, build from documents, FalkorDB vs Neo4j, and GraphRAG tutorial.

Neo4j Cypher Manual introduces the language: "Cypher is Neo4j's declarative query language, allowing expressive and efficient querying of property graphs."

Cypher Decoded adds: "From fundamental Cypher keywords like MATCH, WHERE, and RETURN to powerful data manipulation clauses like CREATE, MERGE, and SET — you're now equipped with the core knowledge to start crafting your own Neo4j queries."

Cypher Query Architecture

flowchart TD subgraph Clauses["Cypher Clauses"] Reading["Reading
MATCH: find patterns
WHERE: filter
RETURN: output
WITH: chain parts
UNION: combine
UNWIND: expand lists"] Writing["Writing
CREATE: new nodes/edges
MERGE: upsert (idempotent)
SET: update properties
DELETE: remove
REMOVE: remove properties
FOREACH: batch ops"] end subgraph Patterns["Pattern Matching"] Node["Node Pattern
(n:Label {prop:value})
variables, labels, props"] Rel["Relationship Pattern
-[r:TYPE]->
direction, type, props"] Path["Path Pattern
(a)-[:KNOWS*2]->(b)
fixed length: {n}
range: *1..3
any: *"] end subgraph Traversal["Graph Traversal"] Fixed["Fixed Hop
-[:TYPE*2]->
exactly n hops"] Range["Range Hop
-[:TYPE*1..3]->
1 to 3 hops"] Any["Any Hop
-[:TYPE*]->
any distance"] Shortest["Shortest Path
shortestPath()
allShortestPaths()
between two nodes"] end subgraph Vector["Vector Search (GraphRAG)"] Index["Vector Index
CREATE INDEX FOR (n:Chunk)
ON (n.embedding)
vector index type"] Query["Vector Query
CALL db.index.vector
.queryNodes(index, k, $vec)
YIELD node, score"] Expand["Graph Expansion
MATCH (node)-[:MENTIONS]->(e)
MATCH (e)<-[:MENTIONS]-(c2)
return more context"] Hybrid["Hybrid Retrieval
vector + fulltext + graph
HybridCypherRetriever
GraphRAG pipeline"] end subgraph Python["Python Integration"] Driver["Neo4j Driver
from neo4j import GraphDatabase
driver.session()
session.run(query, params)"] Falkor["FalkorDB
from falkordb import FalkorDB
graph.query(cypher)
OpenCypher compatible"] Lang["LangChain
Neo4jGraph.query()
GraphCypherQAChain
NL to Cypher"] Llama["LlamaIndex
Neo4jPropertyGraphStore
CypherTemplateRetriever
VectorContextRetriever"] end Reading --> Node Writing --> Rel Node --> Path Rel --> Path Path --> Fixed Path --> Range Path --> Any Path --> Shortest Index --> Query Query --> Expand Expand --> Hybrid Driver --> Lang Falkor --> Llama style Clauses fill:#4169E1,color:#fff style Patterns fill:#39FF14,color:#000 style Traversal fill:#2D1B69,color:#fff style Vector fill:#FF6B6B,color:#fff

Cypher Clause Reference

Clause Purpose Example Use Case
MATCH Find patterns MATCH (p:Person)-[:WORKS_AT]->(o) Query existing data
WHERE Filter results WHERE p.age > 30 AND o.name = 'AI Lab' Conditional filtering
RETURN Specify output RETURN p.name, o.name Query results
CREATE Create nodes/edges CREATE (p:Person {name:'Alice'}) Add new data
MERGE Upsert (idempotent) MERGE (p:Person {name:$name}) SET p.age=$age Ingestion without duplicates
SET Update properties SET p.updated = timestamp() Modify existing data
DELETE Remove nodes/edges MATCH (n) DETACH DELETE n Remove data
ORDER BY Sort results ORDER BY p.name DESC Sorting
LIMIT Restrict count LIMIT 10 Pagination
WITH Chain query parts WITH p, count(o) as orgs Multi-step queries
UNWIND Expand lists UNWIND $names AS name Batch operations
FOREACH Batch operations FOREACH (n IN nodes \| MERGE (n)) Bulk updates

Implementation

from dataclasses import dataclass
from typing import Optional
from enum import Enum

class QueryType(Enum):
    READ = "read"
    WRITE = "write"
    TRAVERSAL = "traversal"
    VECTOR = "vector"

@dataclass
class CypherQueryGuide:
    """Cypher query tutorial implementation guide."""

    def get_basic_queries(self) -> str:
        """Basic Cypher queries."""
        return (
            "// === BASIC CYPHER QUERIES ===\n"
            "\n"
            "// CREATE nodes\n"
            "CREATE (p:Person {\n"
            "  name: 'Dr. Smith',\n"
            "  role: 'Researcher'\n"
            "})\n"
            "CREATE (o:Organization {\n"
            "  name: 'AI Lab',\n"
            "  type: 'research'\n"
            "})\n"
            "\n"
            "// CREATE relationships\n"
            "MATCH (p:Person {name:'Dr. Smith'}),\n"
            "      (o:Organization {name:'AI Lab'})\n"
            "CREATE (p)-[:WORKS_AT {since: 2020}]->(o)\n"
            "\n"
            "// MERGE (idempotent upsert)\n"
            "MERGE (p:Person {name: $name})\n"
            "  ON CREATE SET p.created = timestamp()\n"
            "  ON MATCH SET p.updated = timestamp()\n"
            "SET p.role = $role\n"
            "\n"
            "// MATCH with WHERE\n"
            "MATCH (p:Person)-[:WORKS_AT]->(o:Organization)\n"
            "WHERE o.name = 'AI Lab'\n"
            "  AND p.role = 'Researcher'\n"
            "RETURN p.name, p.role, o.name\n"
            "ORDER BY p.name\n"
            "LIMIT 10\n"
            "\n"
            "// WITH chaining\n"
            "MATCH (p:Person)-[:WORKS_AT]->(o)\n"
            "WITH o, count(p) as employee_count\n"
            "WHERE employee_count > 5\n"
            "RETURN o.name, employee_count\n"
            "ORDER BY employee_count DESC\n"
            "\n"
            "// UNWIND for batch\n"
            "UNWIND $people AS person\n"
            "MERGE (p:Person {name: person.name})\n"
            "SET p.role = person.role"
        )

    def get_traversal_queries(self) -> str:
        """Multi-hop traversal queries."""
        return (
            "// === GRAPH TRAVERSAL ===\n"
            "\n"
            "// Fixed length: exactly 2 hops\n"
            "MATCH (p:Person)-[:KNOWS*2]->(friend)\n"
            "RETURN p.name, friend.name\n"
            "\n"
            "// Range: 1 to 3 hops\n"
            "MATCH (p:Person)-[:KNOWS*1..3]->(contact)\n"
            "WHERE contact.name <> p.name\n"
            "RETURN DISTINCT p.name, contact.name\n"
            "\n"
            "// Any distance\n"
            "MATCH (p:Person {name:'Alice'})\n"
            "      -[:KNOWS*]->(contact)\n"
            "RETURN DISTINCT contact.name\n"
            "LIMIT 50\n"
            "\n"
            "// Quantified relationship\n"
            "// up to 5 hops, type KNOWS\n"
            "MATCH (a:Person {name:'Anna'})\n"
            "      -[:KNOWS*1..5]->(p:Person)\n"
            "WHERE p.age < a.age\n"
            "RETURN DISTINCT p.name\n"
            "\n"
            "// Shortest path\n"
            "MATCH (a:Person {name:'Alice'}),\n"
            "      (b:Person {name:'Bob'}),\n"
            "      p = shortestPath(\n"
            "        (a)-[:KNOWS*]-(b))\n"
            "RETURN p\n"
            "\n"
            "// All shortest paths\n"
            "MATCH (a:Person {name:'Alice'}),\n"
            "      (b:Person {name:'Bob'}),\n"
            "      p = allShortestPaths(\n"
            "        (a)-[:KNOWS*]-(b))\n"
            "RETURN p\n"
            "\n"
            "// Multi-hop with filtering\n"
            "MATCH path = (p:Person)\n"
            "  -[:WORKS_AT]->(o:Organization)\n"
            "  -[:PARTNER_OF]->(o2:Organization)\n"
            "WHERE o.name = 'AI Lab'\n"
            "RETURN p.name, o.name, o2.name\n"
            "\n"
            "// Path with relationships\n"
            "MATCH p = (a)-[:KNOWS*1..3]->(b)\n"
            "RETURN relationships(p) AS rels,\n"
            "       nodes(p) AS nodes"
        )

    def get_vector_search(self) -> str:
        """Vector search for GraphRAG."""
        return (
            "// === VECTOR SEARCH (GraphRAG) ===\n"
            "\n"
            "// Create vector index\n"
            "CREATE VECTOR INDEX chunk_embeddings\n"
            "FOR (c:Chunk) ON (c.embedding)\n"
            "OPTIONS {\n"
            "  indexConfig: {\n"
            "    `vector.dimensions`: 384,\n"
            "    `vector.similarity_function`:\n"
            "      'cosine'\n"
            "  }\n"
            "}\n"
            "\n"
            "// Vector similarity search\n"
            "CALL db.index.vector.queryNodes(\n"
            "  'chunk_embeddings',\n"
            "  5,  // top k\n"
            "  $query_vector\n"
            ")\n"
            "YIELD node, score\n"
            "RETURN node.text AS text, score\n"
            "ORDER BY score DESC\n"
            "\n"
            "// GraphRAG: vector + graph expansion\n"
            "CALL db.index.vector.queryNodes(\n"
            "  'chunk_embeddings',\n"
            "  5, $query_vector\n"
            ")\n"
            "YIELD node AS seed, score\n"
            "MATCH (seed)-[:MENTIONS]->(e)\n"
            "MATCH (e)<-[:MENTIONS]-(c2:Chunk)\n"
            "RETURN DISTINCT\n"
            "  c2.text AS context,\n"
            "  collect(e.name) AS entities,\n"
            "  score\n"
            "LIMIT 20\n"
            "\n"
            "// Hybrid: vector + fulltext\n"
            "CALL {\n"
            "  CALL db.index.vector.queryNodes(\n"
            "    'chunk_embeddings',\n"
            "    5, $query_vector\n"
            "  ) YIELD node, score\n"
            "  RETURN node, score\n"
            "  UNION\n"
            "  CALL db.index.fulltext.queryNodes(\n"
            "    'chunk_text',\n"
            "    $query_text\n"
            "  ) YIELD node, score\n"
            "  RETURN node, score\n"
            "}\n"
            "WITH node, collect(score) AS scores\n"
            "MATCH (node)-[:MENTIONS]->(e)\n"
            "RETURN node.text, e.name,\n"
            "  max(scores) AS best_score"
        )

    def get_python_integration(self) -> str:
        """Python neo4j driver integration."""
        return (
            "# === PYTHON INTEGRATION ===\n"
            "from neo4j import GraphDatabase\n"
            "\n"
            "driver = GraphDatabase.driver(\n"
            "    'bolt://localhost:7687',\n"
            "    auth=('neo4j', 'password'))\n"
            "\n"
            "# 1. Simple query\n"
            "with driver.session() as sess:\n"
            "    result = sess.run(\n"
            "        '''MATCH (p:Person)\n"
            "          -[:WORKS_AT]->(o)\n"
            "          WHERE o.name = $org\n"
            "          RETURN p.name''',\n"
            "        org='AI Lab')\n"
            "    for record in result:\n"
            "        print(record['p.name'])\n"
            "\n"
            "# 2. Parameterized MERGE\n"
            "with driver.session() as sess:\n"
            "    sess.run(\n"
            "        '''MERGE (p:Person\n"
            "          {name: $name})\n"
            "          SET p.role = $role''',\n"
            "        name='Dr. Smith',\n"
            "        role='Researcher')\n"
            "\n"
            "# 3. Transaction (write)\n"
            "def add_person(tx, name, role):\n"
            "    tx.run(\n"
            "        '''MERGE (p:Person\n"
            "          {name: $name})\n"
            "          SET p.role = $role''',\n"
            "        name=name, role=role)\n"
            "\n"
            "with driver.session() as sess:\n"
            "    sess.execute_write(\n"
            "        add_person,\n"
            "        'Alice', 'Engineer')\n"
            "\n"
            "# 4. Batch with UNWIND\n"
            "with driver.session() as sess:\n"
            "    sess.run(\n"
            "        '''UNWIND $people AS person\n"
            "          MERGE (p:Person\n"
            "            {name: person.name})\n"
            "          SET p.role = person.role''',\n"
            "        people=[\n"
            "            {'name':'Alice',\n"
            "             'role':'Engineer'},\n"
            "            {'name':'Bob',\n"
            "             'role':'Designer'},\n"
            "        ])\n"
            "\n"
            "# 5. FalkorDB (OpenCypher)\n"
            "from falkordb import FalkorDB\n"
            "db = FalkorDB(\n"
            "    host='localhost', port=6379)\n"
            "graph = db.select_graph('mygraph')\n"
            "result = graph.query(\n"
            "    '''MATCH (p:Person)\n"
            "      -[:WORKS_AT]->(o)\n"
            "      RETURN p.name, o.name''')\n"
            "for row in result.result_set:\n"
            "    print(row)\n"
            "\n"
            "driver.close()"
        )

    def get_graphrag_queries(self) -> str:
        """GraphRAG-specific Cypher queries."""
        return (
            "// === GRAPHRAG CYPHER ===\n"
            "\n"
            "// Schema: Document→Chunk→Entity\n"
            "// Find entities mentioned in chunks\n"
            "MATCH (d:Document)\n"
            "      -[:CONTAINS]->(c:Chunk)\n"
            "      -[:MENTIONS]->(e)\n"
            "WHERE d.id = $doc_id\n"
            "RETURN e.name, e.type,\n"
            "       count(c) AS mentions\n"
            "ORDER BY mentions DESC\n"
            "\n"
            "// Multi-hop: person → org → partners\n"
            "MATCH (p:Person)\n"
            "  -[:WORKS_AT]->(o:Organization)\n"
            "  -[:PARTNER_OF]->(o2:Organization)\n"
            "RETURN p.name, o.name, o2.name\n"
            "\n"
            "// Community detection results\n"
            "MATCH (c:Community)\n"
            "      -[:HAS_MEMBER]->(e:Entity)\n"
            "WHERE c.level = 2\n"
            "RETURN c.name AS community,\n"
            "       collect(e.name) AS members\n"
            "\n"
           ">// Temporal query (Graphiti-style)\n"
            "MATCH (p:Person {name:'Alice'})\n"
            "      -[r:WORKS_AT]->(o)\n"
            "WHERE r.valid_at <= $point_in_time\n"
            "  AND (r.invalid_at IS NULL\n"
            "       OR r.invalid_at > $point_in_time)\n"
            "RETURN o.name AS employer_at_time\n"
            "\n"
            "// Provenance: trace fact to source\n"
            "MATCH (e:Entity {name:'Dr. Smith'})\n"
            "      <-[:MENTIONS]-(c:Chunk)\n"
            "      <-[:CONTAINS]-(d:Document)\n"
            "RETURN d.title, d.url, c.text\n"
            "\n"
            "// Aggregation: entity co-occurrence\n"
            "MATCH (c:Chunk)\n"
            "      -[:MENTIONS]->(e1:Entity),\n"
            "      (c)-[:MENTIONS]->(e2:Entity)\n"
            "WHERE e1.name < e2.name\n"
            "RETURN e1.name, e2.name,\n"
            "       count(c) AS co_occurrences\n"
            "ORDER BY co_occurrences DESC\n"
            "LIMIT 20"
        )

    def get_clause_reference(self) -> dict:
        """Cypher clause reference."""
        return {
            "MATCH": "Find patterns in graph — nodes and relationships matching specified labels and properties",
            "WHERE": "Filter results by conditions — supports AND, OR, NOT, IN, EXISTS, string matching",
            "RETURN": "Specify what to return — nodes, relationships, properties, aggregations",
            "CREATE": "Create new nodes and relationships — fails if already exists (use MERGE for upsert)",
            "MERGE": "Create if not exists, match if exists — idempotent upsert, use ON CREATE SET / ON MATCH SET",
            "SET": "Update properties on nodes and relationships — can set multiple properties at once",
            "DELETE": "Remove nodes or relationships — DETACH DELETE removes node and all its relationships",
            "ORDER_BY": "Sort results by property — ASC (default) or DESC",
            "LIMIT": "Restrict number of returned results — often combined with ORDER BY",
            "WITH": "Chain query parts — pipes output of one part as input to next, like Unix pipe",
            "UNION": "Combine results from multiple queries — must have same column names",
            "UNWIND": "Expand list into individual rows — useful for batch operations",
            "FOREACH": "Execute operations for each item in list — used for bulk updates",
            "CALL": "Call stored procedures — db.index.vector.queryNodes, db.index.fulltext.queryNodes",
        }

Cypher Query Checklist

  • [ ] Cypher: Neo4j declarative query language for property graphs — expressive and efficient
  • [ ] MATCH: find patterns in graph (nodes and relationships matching labels/properties)
  • [ ] WHERE: filter results by conditions (AND, OR, NOT, IN, EXISTS, string matching)
  • [ ] RETURN: specify output — nodes, relationships, properties, aggregations
  • [ ] CREATE: create new nodes and relationships — use for initial data, not ingestion
  • [ ] MERGE: idempotent upsert — create if not exists, match if exists
  • [ ] MERGE: ON CREATE SET / ON MATCH SET for conditional property updates
  • [ ] MERGE: always use for ingestion to avoid duplicates (not CREATE)
  • [ ] MERGE: use unique constraints for performance (MERGE locks the node)
  • [ ] SET: update properties on nodes and relationships
  • [ ] DELETE: remove nodes or relationships — DETACH DELETE removes node + all relationships
  • [ ] ORDER BY: sort results — ASC (default) or DESC
  • [ ] LIMIT: restrict number of results — combine with ORDER BY for top-k
  • [ ] WITH: chain query parts — pipes output as input to next part
  • [ ] UNION: combine results from multiple queries — same column names required
  • [ ] UNWIND: expand list into individual rows — batch operations
  • [ ] FOREACH: execute operations for each item in list — bulk updates
  • [ ] CALL: call stored procedures — db.index.vector.queryNodes, db.index.fulltext.queryNodes
  • [ ] Node pattern: (variable:Label {prop: value}) — variables, labels, properties
  • [ ] Relationship pattern: -[variable:TYPE {prop: value}]-> — direction, type, properties
  • [ ] Fixed-length traversal: -[:TYPE*2]-> exactly n hops
  • [ ] Range traversal: -[:TYPE*1..3]-> 1 to 3 hops
  • [ ] Any-distance traversal: -[:TYPE*]-> any number of hops
  • [ ] Quantified relationship: -[:KNOWS*1..5]-> with WHERE filtering during traversal
  • [ ] Shortest path: shortestPath((a)-[:TYPE*]-(b)) — single shortest path
  • [ ] All shortest paths: allShortestPaths((a)-[:TYPE*]-(b)) — all shortest paths
  • [ ] DISTINCT: ensure unique results in RETURN
  • [ ] relationships(p): get relationships from a path
  • [ ] nodes(p): get nodes from a path
  • [ ] Vector index: CREATE VECTOR INDEX ... FOR (n:Label) ON (n.embedding) with dimensions and similarity function
  • [ ] Vector query: CALL db.index.vector.queryNodes('index', k, $vector) YIELD node, score
  • [ ] GraphRAG retrieval: vector seeds → graph expansion via MENTIONS → return context + entities + scores
  • [ ] Hybrid retrieval: vector + fulltext via UNION CALL — combine semantic and keyword matching
  • [ ] HybridCypherRetriever: vector + fulltext index, retrieval query traverses graph for more context
  • [ ] Schema queries: Document→Chunk→Entity with CONTAINS and MENTIONS relationships
  • [ ] Multi-hop: Person→Organization→Partner for multi-hop reasoning
  • [ ] Community: Community→HAS_MEMBER→Entity for community-level queries
  • [ ] Temporal: WHERE r.valid_at <= $time AND (r.invalid_at IS NULL OR r.invalid_at > $time)
  • [ ] Provenance: trace Entity←MENTIONS←Chunk←CONTAINS←Document to source
  • [ ] Co-occurrence: count chunks mentioning both entities for entity co-occurrence analysis
  • [ ] Python: from neo4j import GraphDatabase, driver.session(), session.run(query, params)
  • [ ] Python: session.execute_write(tx_func) for transactional writes
  • [ ] Python: parameterized queries with $param syntax for safety
  • [ ] Python: batch with UNWIND $list AS item
  • [ ] FalkorDB: from falkordb import FalkorDB, graph.query(cypher) — OpenCypher compatible
  • [ ] LangChain: Neo4jGraph.query(), GraphCypherQAChain for NL to Cypher
  • [ ] LlamaIndex: Neo4jPropertyGraphStore, CypherTemplateRetriever, VectorContextRetriever
  • [ ] Always close driver: driver.close()
  • [ ] Read knowledge graph basics for concepts
  • [ ] Read build from documents for ingestion
  • [ ] Read FalkorDB vs Neo4j for database comparison
  • [ ] Read GraphRAG tutorial for GraphRAG queries
  • [ ] Test: MATCH returns expected nodes and relationships
  • [ ] Test: MERGE is idempotent — re-running doesn't create duplicates
  • [ ] Test: multi-hop traversal returns correct paths
  • [ ] Test: vector search returns relevant results with scores
  • [ ] Test: hybrid retrieval combines vector + fulltext effectively
  • [ ] Test: parameterized queries prevent injection
  • [ ] Test: batch UNWIND performance for bulk operations
  • [ ] Document query patterns, indexes, traversal strategies, GraphRAG retrieval approach

FAQ

What are the basic Cypher query clauses?

MATCH, WHERE, RETURN, CREATE, MERGE, SET, DELETE, ORDER BY, LIMIT. Neo4j Cypher Manual: "Cypher is Neo4j declarative query language, allowing expressive and efficient querying of property graphs." Cypher Decoded: "From fundamental Cypher keywords like MATCH, WHERE, and RETURN to powerful data manipulation clauses like CREATE, MERGE, and SET — core knowledge to start crafting Neo4j queries." Basic clauses: (1) MATCH: find patterns in graph (nodes and relationships). (2) WHERE: filter results by conditions. (3) RETURN: specify what to return. (4) CREATE: create nodes and relationships. (5) MERGE: create if not exists (idempotent upsert). (6) SET: update properties on nodes/edges. (7) DELETE: remove nodes or relationships. (8) ORDER BY: sort results. (9) LIMIT: restrict number of results. (10) WITH: chain query parts.

How do you write multi-hop traversal queries in Cypher?

Use variable-length path patterns with quantifiers for multi-hop traversal. Cypher Manual - Core Concepts: "Quantified relationship finds all paths up to 5 hops away, traversing only relationships of type KNOWS. DISTINCT operator ensures RETURN clause only returns unique nodes." Cypher Manual - Basic Queries: "Several ways to search for paths between nodes. Fixed length: specify distance with quantifier {n}. MATCH (p:Person)-[:KNOWS2]->(friend) matches exactly 2 hops. Variable length: -[:KNOWS1..3]-> for 1 to 3 hops. -[:KNOWS]-> for any number of hops." Multi-hop: (1) Fixed: -[:TYPE2]-> exactly 2 hops. (2) Range: -[:TYPE1..3]-> 1 to 3 hops. (3) Any: -[:TYPE]-> any distance. (4) Shortest path: shortestPath((a)-[:TYPE*]-(b)). (5) All paths: allShortestPaths. (6) Filter with WHERE during traversal.

How do you use vector search in Cypher for GraphRAG?

Use db.index.vector.queryNodes for semantic retrieval, then expand with graph traversal. Neo4j GraphRAG Python: "Hybrid Cypher Retriever: results searched in both vector and full-text index. Once similar nodes identified, retrieval query traverses graph and returns more context. HybridCypherRetriever with INDEX_NAME, FULLTEXT_INDEX_NAME, retrieval query." Vector search: (1) Create vector index: CREATE INDEX ... FOR (n:Chunk) ON (n.embedding). (2) Query: CALL db.index.vector.queryNodes('index', k, $vector) YIELD node, score. (3) Expand: MATCH (node)-[:MENTIONS]->(e) MATCH (e)<-[:MENTIONS]-(c2:Chunk). (4) Hybrid: vector + fulltext + graph traversal. (5) Return: chunks + entities + scores. (6) GraphRAG: semantic seeds → graph expansion → re-rank → answer.

How do you use MERGE for idempotent upserts in Cypher?

MERGE creates a node/relationship if it doesn't exist, or matches if it does — idempotent. MERGE: (1) MERGE (n:Person {name: $name}) — creates or matches. (2) SET n.age = $age — updates properties on match or create. (3) MERGE with ON CREATE SET / ON MATCH SET for conditional properties. (4) MERGE (a)-[:KNOWS]->(b) — creates relationship if not exists. (5) MATCH + MERGE for relationships between existing nodes. (6) Always use MERGE (not CREATE) for ingestion to avoid duplicates. (7) MERGE locks the node — use unique constraints for performance. (8) Pattern: MERGE (d:Document {id:$id}) SET d.title=$title. (9) MERGE (c:Chunk {id:$cid}) SET c.text=$text. (10) MATCH (d),(c) MERGE (d)-[:CONTAINS]->(c).

How do you run Cypher queries from Python?

Use neo4j Python driver with session.run or execute_write for transactions. Python: (1) from neo4j import GraphDatabase. (2) driver = GraphDatabase.driver('bolt://localhost:7687', auth=('neo4j','password')). (3) with driver.session() as session: session.run('MATCH (n) RETURN n'). (4) Parameterized: session.run('MATCH (p:Person {name:$name}) RETURN p', name='Alice'). (5) Transactions: session.execute_write(tx_func). (6) Results: for record in result: record['p.name']. (7) FalkorDB: from falkordb import FalkorDB, graph.query('Cypher'). (8) LangChain: Neo4jGraph.query() or GraphCypherQAChain. (9) LlamaIndex: Neo4jPropertyGraphStore. (10) Always close driver: driver.close().


Want a self-hosted AI company brain that does all of this out of the box?
Book a demo →