Repository navigation
Description
Summary
$param is parsed as a field name; other syntaxes (@param, :param, {param}, ?) that users coming from other databases may be familiar with, also fail. with_parameter also doesn’t help in Python, even though the Rust docs state that it exists.
Currently in lance-graph, it's necessary do custom string substitution before the parameterized Cypher query ever reaches the parser.
For example, in query.py, execute_query() calls apply_params(), which replaces $city, $country, etc. with literal values (e.g., 'London') in the query string. So Lance Graph never sees $city; it only sees a fully inlined literal, which is supported.
So the engine itself still doesn’t support placeholders — we’re just emulating parameters upstream in Python. Can we do better?
Example
import pyarrow as pa
from lance_graph import GraphConfig, CypherQuery
people = pa.table({"id": [1, 2], "age": [30, 40]})
cfg = GraphConfig.builder().with_node_label("Person", "id").build()
datasets = {"Person": people}
query = "MATCH (p:Person) WHERE p.age > $age RETURN p.id"
CypherQuery(query).with_parameter("age", 35).with_config(cfg).execute(datasets)Traceback (most recent call last):
File "/Users/prrao/code/graph-benchmark/lance_graph/tt.py", line 9, in <module>
CypherQuery(query).with_parameter("age", 35).with_config(cfg).execute(datasets)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^
ValueError: Query planning error: Failed to build filter: Schema error: No field named "$age". Did you mean 'p__age'?.
Expected
Returns id=2, when the age value is passed as a Cypher parameter.
Actual
When we attempt the example above, we get the following:
ValueError: ... No field named "$age".
A lot of custom boilerplate was needed to be written (see below) to correctly parse the parameters into a syntax that naturally maps to other systems:
def format_cypher_value(value: Any) -> str:
if isinstance(value, str):
escaped = value.replace("'", "''")
return f"'{escaped}'"
if isinstance(value, bool):
return "true" if value else "false"
if value is None:
return "null"
return str(value)
def apply_params(query: str, params: dict[str, Any]) -> str:
# Lance Graph currently doesn't parse $param placeholders; inline values here
# so the query string is fully concrete before parsing.
for key, value in params.items():
query = query.replace(f"${key}", format_cypher_value(value))
return query
def execute_query(
query: str,
cfg: GraphConfig,
datasets: dict[str, pa.Table],
params: dict[str, Any] | None = None,
) -> pl.DataFrame:
if params:
# Inline params instead of using CypherQuery.with_parameter, which isn't
# respected by the current parser.
query = apply_params(query, params)
cypher = CypherQuery(query)
result = cypher.with_config(cfg).execute(datasets)
return to_polars(result)
# use in query
result = execute_query(query, cfg, datasets, params=params)Proposal
Currently the user has to inline parameters manually (string substitution) to proceed. We should support parameterized query execution in Cypher more naturally, like other graph DBs, to make writing queries more natural for users coming from those systems, if possible.
- DuckDB does this via prepared statements
- Neo4j does this via parameters
Environment
The following environment was used to test this:
lance-graph 0.4.0
Python 3.13
macOS Tahoe 26.2
Activity
ChunxuTang commentedon Jan 29, 2026
Hi @leiyuou, do you want to take a look at this issue?
I previously left some ideas to implement it at https://github.com/lance-format/lance-graph/blob/main/crates/lance-graph/src/datafusion_planner/expression.rs#L296. Hope that could be helpful.
Note that after implementing the support of parameters on the Rust side, we also need to expose and support it on the Python side.
leiyuou commentedon Feb 2, 2026
I will work on it
prrao87 commentedon Feb 2, 2026
Thank you! I think this will improve performance on some query workloads in the LDBC benchmark - I see that lance-graph is far slower, by around 10x, than other alternatives. Can't think of a good reason why - I can only pinpoint this to Python overheads in preparing and executing the queries via parameters passed in from Python.
[-]Parameter placeholders don’t work (even though `with_parameter` exists in the codebase)[/-][+]feat: Properly support parameter placeholders in Python/Rust[/+]Support with_parameter and with_parameters in query engine (lance-for…
prrao87 commentedon Mar 16, 2026
Hi @ChunxuTang, it's been a while since the last release - could we get a new release with this parameterized Cypher support?
ChunxuTang commentedon Mar 21, 2026
Sure, sounds good to me. Will kick off a release soon today. Cc @beinan