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.
Environment
The following environment was used to test this:
lance-graph 0.4.0
Python 3.13
macOS Tahoe 26.2
Summary
$paramis parsed as a field name; other syntaxes (@param,:param,{param},?) that users coming from other databases may be familiar with, also fail.with_parameteralso 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()callsapply_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
Expected
Returns
id=2, when theagevalue 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:
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.
Environment
The following environment was used to test this: