Skip to content

Support directional chain hops in anti-edge count rewrite - #921

Merged
adsharma merged 2 commits into
packed_extend_chainsfrom
anti-edge-count-directional
Sep 6, 2026
Merged

adsharma merged 2 commits into
packed_extend_chainsfrom
anti-edge-count-directional

Conversation

@adsharma

@adsharma adsharma commented Sep 5, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Directional chain support. The anti-edge count rewrite previously required both triangle chain hops to be BOTH-direction extends, so LSQB q9 as written in the benchmark ((p1)-[:KNOWS]->(p2)-[:KNOWS]->(p3) with a directed anti-edge) never fired and ran a ~2.2s hash join plan. The T - S - A count arithmetic is now parameterized by the enumeration direction of each chain hop and of the anti-edge match (any FWD/BWD/BOTH combination):

  • T = all chain tuples, weighted by per-node suffix path counts D
  • S = n0 = n2 tuples (excluded by id(n0) <> id(n3))
  • A = tuples where the anti-edge row exists, with row partners deduped per key pair (the mark is boolean)

Chain enumeration is row-level (a BOTH hop enumerates FWD rows then BWD rows, no dedup) to match the extend operator's semantics. Directions are recorded in LogicalCountAntiEdgeChain and plumbed through the plan mapper into the physical operator. Also fixes two latent bugs in the shape matcher (scanNode->getNodeID() is the internal-ID property, so ._ID-suffixed unique names never matched plain node variable names) plus build errors/warnings, and removes the LBUG_TRACE_ANTI trace scaffolding.

LBUG_DUMP_LOGICAL. The logical plan tree dumper is moved out of count_rel_table_optimizer.cpp into Optimizer::optimize, which every query goes through. With LBUG_DUMP_LOGICAL=1 the logical plan prints to stderr as a readable indented tree (one operator per line: extend directions, join keys/types, filter predicates, aggregate keys, scan targets) both before and after optimization — much easier to read than EXPLAIN's ASCII-art boxes.

Validation

  • LSQB sf-like database, q9.cypher: fires the operator, returns the same count as the join plan (268837983); execution ~2219ms → ~23ms (~95x).
  • Six direction combos (FWD/BWD/BOTH chains × FWD/BOTH anti-edge) cross-checked against the join-plan ground truth on the same database.
  • LBUG_DUMP_LOGICAL smoke-tested on DDL, inserts, and reads.

@adsharma
adsharma force-pushed the anti-edge-count-directional branch 2 times, most recently from 28c7544 to 335b4f5 Compare September 5, 2026 23:02
@adsharma adsharma changed the title Support directional chain hops in anti-edge count rewrite; generalize LBUG_DUMP_LOGICAL Support directional chain hops in anti-edge count rewrite Sep 5, 2026
CountAntiEdgeChain previously required the two triangle chain hops to be
BOTH-direction extends, so LSQB q9 (as written in the benchmark, with
directed knows edges) never fired the rewrite and fell back to a ~2.2s
hash join plan.

- Generalize the T - S - A count arithmetic to any FWD/BWD/BOTH
  combination of chain hop directions and anti-edge match direction.
  Chain enumeration is row-level (a BOTH hop enumerates FWD rows then
  BWD rows, no dedup), the anti-edge mark is boolean per key pair so
  row partners are deduped when subtracting A.
- Record the hop/anti-edge directions in LogicalCountAntiEdgeChain and
  plumb them through the plan mapper into the physical operator.
- Fix the mid-node resolution in the optimizer: scanNode->getNodeID()
  is the internal-ID property (uniqueName has the ._ID suffix), so the
  mid NodeExpression is now recovered from the chain extends and names
  are compared as plain variable names (same fix in the suffix scan
  check).
- Fix build errors (const Transaction*, containsOp declaration) and
  warnings (dangling reference, unused variable).
- Remove the LBUG_TRACE_ANTI trace scaffolding.

Validation on the LSQB sf-like database: q9.cypher now fires the
operator and returns the same count as the join plan (268837983);
execution drops from ~2219ms to ~23ms (~95x). Six direction combos
were cross-checked against the join-plan ground truth.
Move the logical plan tree dumper out of count_rel_table_optimizer.cpp
into Optimizer::optimize, which every query goes through. With
LBUG_DUMP_LOGICAL set in the environment, the logical plan is printed
to stderr as a readable indented tree (one operator per line, with
extend directions, join keys/types, filter predicates, aggregate keys
and scan targets) both before and after optimization.
@adsharma
adsharma force-pushed the anti-edge-count-directional branch from 335b4f5 to 23a2b3e Compare September 6, 2026 01:40
@adsharma
adsharma merged commit 346865d into main Sep 6, 2026
4 checks passed
@adsharma
adsharma deleted the anti-edge-count-directional branch September 6, 2026 19:36
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