How the Cypher Query Engine Works in codebase-memory-mcp: OpenCypher Support Explained
The Cypher query engine in codebase-memory-mcp is a read-only interpreter that translates a subset of OpenCypher into SQLite SQL, executing it against the internal knowledge-graph store through a four-stage pipeline of lexing, parsing, planning, and execution.
The cypher query engine provides a lightweight, self-contained query layer for the cbm_store knowledge graph in the DeusData/codebase-memory-mcp repository. Unlike full Neo4j implementations, this engine deliberately restricts operations to read-only graph analysis, converting pattern matching and aggregation expressions into executable SQL statements.
Four-Stage Query Processing Pipeline
The engine processes queries through a strict pipeline implemented primarily in src/cypher/cypher.c.
Lexer: Tokenizing the Input
The lexer scans input strings and produces a token stream containing identifiers, literals, operators, and punctuation. It handles string escaping, numeric literals, comments, and two-character tokens such as !=, <=, and ...
Key implementation resides in src/cypher/cypher.c at lines 68-124.
Parser: Building the Abstract Syntax Tree
A recursive-descent parser constructs an abstract syntax tree (AST) for MATCH patterns, WHERE clauses, and RETURN specifications. The parser detects unsupported clauses early and validates syntax against the read-only subset.
Implementation spans lines 329-782 in src/cypher/cypher.c.
Planner: Generating SQL
The planner walks the AST and produces equivalent SQL SELECT statements. It maps node variables to columns (name, qn, label, file) and edge variables to columns (name, qn, label), generating necessary joins and filters for the SQLite backend.
Executor: Running Against SQLite
The executor sends generated SQL to the SQLite-backed cbm_store via cbm_exec_query in src/store/store.c, returning result sets to the caller.
Supported OpenCypher Features
The cypher query engine supports a practical read-only subset of OpenCypher optimized for code analysis.
MATCH Patterns
- Node syntax:
(n:Label)with optional variable names - Relationship syntax:
-[:TYPE*min..max]->with hop ranges - Label alternation:
:A|B|Cparsed as pipe-separated lists (stored as"A|B|C"and split during matching; seeparse_node()around line 560) - Hop ranges:
*2..5for specific depths;*alone translates to1..0(where0signals no upper bound)
Example:
const char *cypher = "MATCH (n:Class|Interface)-[:CALLS*1..3]->(m) "
"WHERE n.name CONTAINS \"Get\" "
"RETURN n.name, COUNT(m) AS calls "
"ORDER BY calls DESC LIMIT 10";
WHERE Filters
Supported operators include:
- Logical:
AND,OR,XOR,NOT - Comparison:
=,<>,>,<,>=,<= - String:
CONTAINS,STARTS WITH,ENDS WITH - Null checking:
IS NULL,IS NOT NULL - List membership:
IN - Label tests:
n:Label
RETURN Clause
Supports scalar expressions, aggregate functions (COUNT, SUM, AVG, MIN, MAX, COLLECT), DISTINCT, and string functions (toLower, toUpper, toString). Multi-argument scalar functions include coalesce, substring, replace, left, and right.
Function handling distinguishes three groups via is_aggregate_tok(), is_string_func_tok(), and is_multiarg_func_call() around lines 1292-1302.
Ordering and Pagination
Full support for ORDER BY ... ASC|DESC, SKIP, and LIMIT.
EXISTS Predicate
Limited to single-hop patterns: (var)-[:TYPE]->().
Example:
const char *cypher2 = "MATCH (e) "
"WHERE EXISTS { (e)-[:EXTENDS]->() } "
"RETURN e.name, "
" CASE WHEN e.is_public THEN \"public\" ELSE \"private\" END AS visibility, "
" toUpper(e.name) AS upper_name";
Property Access and CASE Expressions
Standard node.prop syntax for stored properties and simple CASE ... END expressions.
Variable Limits
Hard-coded constants in src/foundation/limits.h define CYP_MAX_VARS = 16 for node variables and 8 edge variables maximum per query.
Explicitly Unsupported Features
The engine rejects write and schema-altering operations through unsupported_clause_error() (lines 815-838 in src/cypher/cypher.c):
CREATE,DELETE,DETACH,SET,REMOVE,MERGEYIELD,CALL(stored procedures)FOREACH,MANDATORY MATCHDROP,CONSTRAINT
These generate clear error messages such as "unsupported Cypher feature: CREATE clause (write operations not supported)".
Architectural Implementation Details
The expression tree (cbm_expr_t) is built recursively in src/cypher/cypher.c, allowing straightforward evaluation or SQL translation. Label alternation is stored as a single string "A|B|C" and split during pattern matching.
The engine maintains strict boundaries through constants defined in src/foundation/limits.h, ensuring predictable memory usage during query processing.
Practical Code Examples
Multi-hop analysis with label alternation:
const char *cypher = "MATCH (n:Class|Interface)-[:CALLS*1..3]->(m) "
"WHERE n.name CONTAINS \"Get\" "
"RETURN n.name, COUNT(m) AS calls "
"ORDER BY calls DESC LIMIT 10";
Using scalar functions and CASE expressions:
const char *cypher2 = "MATCH (e) "
"WHERE EXISTS { (e)-[:EXTENDS]->() } "
"RETURN e.name, "
" CASE WHEN e.is_public THEN \"public\" ELSE \"private\" END AS visibility, "
" toUpper(e.name) AS upper_name";
Filtering with IN lists and IS NULL:
const char *cypher3 = "MATCH (c) "
"WHERE c.type IN [\"class\", \"enum\"] AND c.deprecated IS NULL "
"RETURN c.name, c.type";
Key Source Files
| File | Role |
|---|---|
src/cypher/cypher.h |
Public API for lexing, parsing, and execution |
src/cypher/cypher.c |
Full lexer, parser, AST construction, and query planning |
src/store/store.c |
SQLite-backed store receiving generated SQL via cbm_exec_query |
src/foundation/limits.h |
Engine constants including CYP_MAX_VARS |
src/cli/cli.c |
CLI wrapper exposing the query_graph command |
Summary
- The cypher query engine implements a four-stage pipeline: lexer, parser, planner, and executor.
- It translates OpenCypher into SQLite SQL for read-only graph queries against
cbm_store. - Supports MATCH patterns with label alternation and hop ranges, WHERE filters, RETURN with aggregates, and ORDER BY/LIMIT.
- Explicitly blocks all write operations (
CREATE,DELETE,SET, etc.) viaunsupported_clause_error(). - Hard limits include 16 node variables and 8 edge variables per query.
- Core implementation resides in
src/cypher/cypher.cwith storage delegation tosrc/store/store.c.
Frequently Asked Questions
Does the codebase-memory-mcp Cypher engine support write operations?
No. The engine is explicitly read-only and rejects CREATE, DELETE, SET, REMOVE, MERGE, and DETACH clauses. The unsupported_clause_error() function in src/cypher/cypher.c (lines 815-838) detects these constructs and returns clear error messages indicating that write operations are not supported.
What is the maximum depth for relationship hop ranges?
The engine supports arbitrary hop ranges using syntax like *2..5. The unbounded * operator translates to 1..0 internally, where 0 signals no upper bound. However, practical limits depend on the underlying SQLite performance and the cbm_store indexing.
How does the engine handle multiple labels in MATCH patterns?
Label alternation such as :Class|Interface is parsed into a single pipe-separated string (e.g., "Class|Interface") and stored as such. During execution, this string is split and evaluated as a label test. This implementation is found in parse_node() around line 560 of src/cypher/cypher.c.
Which aggregate functions are available in RETURN clauses?
The engine supports COUNT, SUM, AVG, MIN, MAX, and COLLECT. These are identified by is_aggregate_tok() around line 1292 in src/cypher/cypher.c, alongside string functions like toLower and toUpper, and multi-argument functions such as coalesce and substring.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →