Skip to content

Conversation

@aheev
Copy link

@aheev aheev commented Feb 6, 2026

This PR adds support for re-using variables in a MATCH clause with caveats below:

  • re-use is supported only on nodes, not rels
  • variables which arre not previously labelled can't be re-used
  • variable used on a node(label) can't be re-used on a different node(label)

Used label id filter on the nodes to achieve re-use

Added unit and system tests

Closes: #111

@aheev
Copy link
Author

aheev commented Feb 6, 2026

@beinan @ChunxuTang could you PTAL? Curious why are we passing query in plan rather than constructor?

@codecov-commenter
Copy link

Codecov Report

❌ Patch coverage is 94.11765% with 12 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
crates/lance-graph/src/logical_plan.rs 94.08% 12 Missing ⚠️

📢 Thoughts on this report? Let us know!

Copy link
Collaborator

@beinan beinan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great contribution! just left some minor comments. @ChunxuTang do you want to take a look also?

property: self
.config
.get_node_mapping(&target_label)
.unwrap()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unwrap() will panic if the label exists in the query but isn’t present in the mapping (or
if lookup is case‑sensitive and misses). Can we return a PlanError instead (or fall back to default_node_id_field)? This is a hard
crash in planning.


impl LogicalPlanner {
pub fn new() -> Self {
pub fn new(config: GraphConfig) -> Self {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is a breaking change @ChunxuTang do you think it's ok?

.clone()
.unwrap_or_else(|| format!("_node_{}", self.variables.len()));
// Determine target variable and check for reuse
// TODO: create error types for these cases
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we can fix in another pr. would it be better to define dedicated error variants for reuse issues (unlabeled reuse, label mismatch, rel reuse)? Makes them easier to test/match and avoids string matching.


// Phase 2: Graph Logical Plan
let mut logical_planner = LogicalPlanner::new();
let mut logical_planner = LogicalPlanner::new(config.clone());
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: adds another clone of GraphConfig. Probably fine, but if we want to avoid extra clones, we could accept &GraphConfig or Arc in the planner.

@beinan
Copy link
Collaborator

beinan commented Feb 11, 2026

Curious why are we passing query in plan rather than constructor?

Because the planner is stateful across a single plan run, not across multiple queries. plan(&CypherQuery) keeps the planner reusable and makes it clear that the query is per‑call input. The constructor is used for long‑lived config/state (like GraphConfig or counters), while the query is a transient input. If we put the query in the constructor, we’d either (a) need a new planner per query anyway, or (b) store the query inside and prevent reusing the planner for another query. Passing it to plan is the more flexible pattern and matches how SemanticAnalyzer/DataFusionPlanner are used.

@ChunxuTang
Copy link
Collaborator

@beinan Per discussion in this thread #111 (comment), I think it's better to reject self-reference queries and encourage users to rewrite the query to fit better for columnar storage. In the longer term, it's better to have a query rewritter component to automatically rewrite self-reference queries.

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.

Self-pointing Cypher pattern causes lance-graph to hang indefinitely for high cardinality datasets

4 participants