Skip to content

fix(query): support bound string predicates and untyped relationships across labels - #120

Merged
prrao87 merged 3 commits into
mainfrom
fix/bound-string-predicates
Nov 4, 2026
Merged

prrao87 merged 3 commits into
mainfrom
fix/bound-string-predicates

Conversation

@prrao87

@prrao87 prrao87 commented Nov 4, 2026

Copy link
Copy Markdown
Member

Why

Two kinds of Cypher query that applications send still fail on 0.7.0:

  • String predicates with a bound right-hand side, such as MATCH (p:Person) WHERE p.name CONTAINS $name RETURN p, fail with a parse error at WHERE. CONTAINS, STARTS WITH and ENDS WITH only accepted a quoted literal.
  • Patterns with an untyped relationship and unlabeled nodes, such as MATCH (a)-[]-(b) RETURN a, b, fail with Node label 'Node' doesn't exist.

This brings in the two fixes from the codex/bound-string-predicates branch by @stugorf, rebased onto current main (after the planner and CSR changes in #87, #109, #113, #115 and #118), plus one planner fix for a regression the rebase exposed.

What changed

Bound string predicates. The right-hand side of CONTAINS, STARTS WITH and ENDS WITH can be any expression: a parameter, a property or a function call. Matching is now an exact, case-sensitive substring, prefix or suffix check for literals too, instead of a rewrite to LIKE.

This changes results for some queries that already ran:

Query 0.7.0 Now
p.name CONTAINS '50%' % is a wildcard matches the literal text 50%
p.name STARTS WITH 'a_b' _ matches any character matches the literal text a_b
p.age CONTAINS 'x' (non-string operand) not checked null, so the row is filtered out

An empty search string matches every non-null string. id, or a label's id field, is now also accepted as a property on a variable with several labels.

Untyped relationships across labels. An omitted relationship type ranges over every configured relationship type. Unlabeled endpoints resolve from each relationship's source and target mappings, without combining identities from different node tables. Undirected matches include both orientations, a self-loop appears once, and parallel edges stay distinct. Whole-node returns in these queries come back as typed entity records, with or without the CSR.

Reused relationship variables. Untyped-pattern detection now ignores a relationship variable that is written with a type elsewhere in the query, as in MATCH (a)-[r:KNOWS]->(b), (c)-[r]->(d). Without this, the rebased fix sent those queries down the untyped path, and they failed with duplicate unqualified field name r.

docs/cypher.md documents both features.

Validation

  • cargo test --workspace: all suites pass, including the new engine tests for both fixes, with and without the CSR.
  • cargo clippy --workspace --all-targets -- -D warnings and cargo fmt --all --check: clean.
  • The embedded_query example runs.
  • Python: pytest -n auto python/tests/ --doctest-modules python/lance_csr passes (1704 tests), including the new test_string_parameters.py and test_engine_alternation.py cases. ruff and pyright are clean.
  • Not measured: literal string filters now go through contains/starts_with/ends_with instead of LIKE. I haven't checked whether Lance still pushes these filters into the scan, or whether benchmarks move.
  • CI is pending.

🤖 Generated with Claude Code

stugorf and others added 3 commits November 3, 2026 20:13
Resolve omitted relationship types from configured endpoints and retain typed boundary-node identities for whole-node returns.
…tern detection

A reused relationship variable such as `(c)-[r]->(d)` takes its type from where `r` was first
written with one (#83). Detecting untyped patterns from the raw AST counted it as untyped and
sent the query down the typed-node-value path, which failed with a duplicate `r` field.
@prrao87
prrao87 marked this pull request as ready for review November 4, 2026 01:34
@prrao87
prrao87 merged commit 2f68752 into main Nov 4, 2026
8 checks passed
@prrao87
prrao87 deleted the fix/bound-string-predicates branch November 4, 2026 01:35
prrao87 added a commit that referenced this pull request Nov 4, 2026
Patch bump: #120 and #121 fix and extend query parsing without changing the Python or Rust API.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants