Skip to content

chore: release lance-csr 0.2.0 - #6

Merged
prrao87 merged 1 commit into
mainfrom
release/0.2.0
Oct 24, 2026
Merged

prrao87 merged 1 commit into
mainfrom
release/0.2.0

Conversation

@prrao87

@prrao87 prrao87 commented Oct 24, 2026 •

Copy link
Copy Markdown
Member

Why

lance-csr 0.2.0 releases what landed since 0.1.0: the official LDBC IC Cypher running as openCypher text (#2), the project goals, the benchmark and uv-based tooling (#4), and the node_table/rel_table config builder (#5). It also brings PyPI back in line with the docs: since #5, the examples use node_table, which 0.1.0 doesn't have.

What changed

  • The version is 0.2.0 everywhere it's recorded: the three published crates, the benches crate and its pin on lance-graph, pyproject.toml, .bumpversion.toml, both Cargo.lock files and uv.lock. The lockfiles change only for lance-csr's own packages.
  • .bumpversion.toml now lists the benches crate. It was missing, and the benches crate's version pin on lance-graph broke the build after the 0.1.0 bump.

The release notes are below, for the GitHub release once this merges.

Validation

  • cargo check --workspace --locked, and the bindings crate with --locked, both build with the updated lockfiles. uv lock --check passes.
  • make install builds lance-csr-0.2.0, importlib.metadata reports 0.2.0, and make test passes 332 tests.
  • The wheel build runs when the release is published: the release workflow checks that the tag matches the package version, v0.2.0, before publishing.

Release notes

lance-csr now runs the official LDBC Interactive complex (IC) queries as openCypher text instead of hand-written ports. Its config builder uses Kuzu-style node and relationship tables, and the project's tooling moves to uv.

Highlights

  • The official LDBC IC Cypher runs as written. 13 of the 14 queries run from the official text (as adapted to the SF1 schema) with no changes. IC14's text uses reduce(), which openCypher's standardisation scope excludes, so it runs as a port built from in-scope constructs. All 14 match the golden answers in every engine mode.
  • New config builder: node_table and rel_table. The names match Kuzu's and Ladybug's node and rel tables, and every relationship table names the node tables at both ends.
  • Faster planning for queries with several subqueries. EXISTS checks, pattern comprehensions and OPTIONAL MATCH no longer copy the query's input exponentially. LDBC IC14 drops from 641 to 225 physical operators and from 188 to 62 ms.

Upgrading

uv add 'lance-csr>=0.2.0'

Rename the builder calls. Each relationship now names its endpoint node tables:

# 0.1
config = (
    GraphConfig.builder()
    .with_node_label("Person", "id")
    .with_node_label("Company", "id")
    .with_relationship("WORKS_AT", "src", "dst")
    .build()
)

# 0.2
config = (
    GraphConfig.builder()
    .node_table("Person", primary_key="id")
    .node_table("Company", primary_key="id")
    .rel_table("WORKS_AT", from_table="Person", to_table="Company", src="src", dst="dst")
    .build()
)

with_node_label, with_relationship and with_relationship_endpoints still work in 0.2 with unchanged behavior. They raise a DeprecationWarning that names the replacement, and will be removed in a later release. In Rust they are marked #[deprecated].

Behavior changes

  • collect() over no values returns [] instead of null, as Cypher specifies.
  • Unaliased variables in RETURN come out under lowercase names (RETURN postCount gives a postcount column), consistent with aliases, which were already lowercased.
  • A list literal that mixes types, such as [uni.name, studyAt.classYear], becomes a list of strings. Before, it failed to cast.
  • The tests and dev extras are gone from the package: development tools now live in a uv dependency group (see below).

New Cypher support

  • Parameters: engine.execute(query, params), and the same for execute_batches and explain, with numbers, strings, booleans, None, and datetime/date values.
  • Nodes as values: a = b, a IN [x, y], count(DISTINCT a) and a IS NULL compare nodes by id, and a path used as a value is its length (path IS NULL).
  • Lists of nodes: collect(node), then UNWIND back to nodes, and x IN list for any list expression (friend IN friends, tag.id IN tags).
  • Chained comparisons: $start <= m.creationDate < $end.
  • Inline properties that read a variable: (t:Tag {id: tagId}).
  • Label inference: an unlabeled node takes the label the relationship's endpoint tables decide, as in (p:Person)<-[:HAS_CREATOR]-(post).
  • Predicates as values: NOT (a)-[:KNOWS]-(b) AS isNew, and CASE path IS NULL WHEN true ....
  • Maps and fields: map literals (a node inside a map keeps its properties), and .field on maps and dates (latestLike.msg.id, birthday.month).
  • Functions: head, toInteger, toFloat, date, datetime and duration.inSeconds.
  • Aggregation: aggregates inside expressions (head(collect(...)), count(x) + 1), and collect() in the order of a preceding WITH ... ORDER BY.
  • Comprehensions: list comprehensions [x IN list WHERE p | v], pattern predicates on the element included, and pattern comprehensions [(a)-[:R]->(b) WHERE p | v].

Fixes

  • Semantic analysis now ends variable scope at WITH, as the planner already did. Before, WITH collect(friend) AS friends UNWIND friends AS friend failed with "redefined with different type".
  • A pattern after WITH may start at a node no earlier clause bound. It used to fail to plan.
  • Variable-length paths kept only their end nodes' columns, dropping scalars and other nodes carried from earlier clauses.
  • An OPTIONAL MATCH whose WHERE reads a list carried by WITH (WHERE friend IN friends) now plans.

Performance

On the LDBC SNB SF1 IC queries as openCypher text (release build, Apple M5), lance-csr's in-memory CSR mode runs 1.96× faster than Neo4j 2026.09 and wins 9 of 14 queries. It runs 4.36× faster than Kuzu 0.11.3 and wins 10 of 14 (geometric means). These are independent, unaudited measurements, not LDBC Benchmark Results, and the Neo4j and Kuzu numbers come from an earlier run on the same machine. The 30-query suite is unchanged from 0.1.0. Its results, next to Neo4j, Kuzu, Ladybug and lance-graph, are in the README and in graph-benchmark-ldbc.

Packaging, documentation and tooling

  • uv instead of pip: dependencies are locked in uv.lock, and development setup is uv sync --locked --no-install-project followed by uv run --no-sync maturin develop. make install, make test and make lint wrap these steps.
  • Documentation: the README states the project's goals: a single-machine, openCypher-compatible engine on Lance, multimodal knowledge graphs, and DataFusion for graph traversal. It marks distributed execution as out of scope, and credits lance-graph and Apache DataFusion. DEVELOPMENT.md and the build steps match the current setup, including the protoc requirement.
  • Release CI: each built wheel is tested by its file path, so PyPI's copy of the same version can't stand in for it. Pull requests build wheels only when the wheel build itself changes.
  • The package metadata names the lance-csr authors.

Known limitations

  • Integer-only list literals such as [1, 2, 3] parse as 32-bit float vectors (the vector-search syntax), so integers above 2^24 lose precision (#3).
  • Unbounded variable-length alternation (-[:A|B*0..]->) unrolls into many branches, and planning them dominates LDBC IC12's run time.

Full changelog: v0.1.0...v0.2.0

🤖 Generated with Claude Code

Also list the benches crate in .bumpversion.toml, whose version pin on
lance-graph otherwise breaks the build after a bump.
@prrao87
prrao87 marked this pull request as ready for review October 24, 2026 16:17
@prrao87
prrao87 merged commit 461c3aa into main Oct 24, 2026
6 checks passed
@prrao87
prrao87 deleted the release/0.2.0 branch October 24, 2026 16:18
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.

1 participant