Skip to content

v1.1.11 - Black Hole Sun

Choose a tag to compare

@orneryd orneryd released this 09 Jul 21:09
· 63 commits to main since this release

[v1.1.11] - Black Hole Sun - 7/9/2026

Added

  • GPU-accelerated HNSW construction on CUDA and Vulkan.
    New pkg/search/hnsw_build_cuda.go and hnsw_build_vulkan.go add optional
    GPU build backends for HNSW graph construction, with matching stubs for
    builds without CUDA/Vulkan support. pkg/gpu/cuda/cuda_bridge.go gains the
    CUDA bridge functions; pkg/gpu/vulkan/compute.go adds a rewritten Vulkan
    compute pipeline with dedicated shaders (hnsw_build_cosine.comp,
    hnsw_build_topk_rows.comp and their compiled SPIR-V). Falls back to CPU
    build when GPU backends are unavailable. Follow-up commit tightens Vulkan
    shader dispatch/binding performance (perf(hnsw): optimize vulkan).
  • Adjacency snapshot-isolation API for storage-backed graph reads.
    Added a new adjacency snapshot-isolation path across pkg/storage and
    pkg/cypher so traversal and delete-adjacent reads resolve visible edges
    against the correct MVCC view instead of relying on coarser snapshot
    behavior. This also threads through namespaced and WAL-wrapped engines and
    adds focused regression coverage in storage and server graph tests.

Fixed

  • Multi-MATCH relationship variable bound in a later clause was silently
    dropped.

    MATCH (s) WHERE s.uid IN $u MATCH (s)-[rel]->() WHERE rel.evidence_source = $e DELETE rel deleted zero edges instead of the
    matching set, and the same shape used as a read (RETURN count(rel),
    RETURN rel, RETURN rel.prop, elementId(rel)) silently returned
    zero/nil for the relationship column while node columns in the same query
    resolved correctly. Root cause: pkg/cypher/match_multi.go's multi-match
    binding row (type binding map[string]*storage.Node) can only hold node
    values, so executeFirstMatch/executeChainedMatch never stored the
    relationship a clause's pattern bound, even though the PathResult
    computed by the traversal already carried it. Kept binding itself
    unchanged (a map[string]*storage.Node value-typed literal is
    constructed/indexed directly by ~10 existing binding-where test files) and
    instead threaded a parallel, index-aligned map[string]*storage.Edge per
    row through executeFirstMatch, executeChainedMatch, a new
    filterBindingsByWhereWithRels (relationship-aware WHERE filtering that
    reuses the unchanged node-only WHERE compiler via a read-only
    bindingWithRelView property adapter), and resolveBindingExpr/
    resolveBindingItem. Also fixed a related gap where executeMultiMatch
    never applied SKIP/LIMIT despite a comment claiming it did. Added
    pkg/cypher/multi_match_relationship_binding_bug_test.go
    (TestBug_MultiMatchRelationshipBindingLost + _Variations,
    TestMergeRelBindings, TestBindingWithRelView,
    TestResolveBindingExprUnboundVariable). Follow-up fixes now also restore
    MATCH ... WITH ... MATCH ... RETURN and ... DELETE rel pipeline shapes
    by delegating valid chained pipeline forms through the general pipeline
    executor, preserving row-bound node/relationship context across later MATCH
    clauses, and trimming trailing ORDER BY / SKIP / LIMIT correctly in
    pipeline RETURN projection.
  • CREATE ... WITH ... query routing now distinguishes missing behavior from
    invalid syntax.

    Valid CREATE ... WITH ... RETURN, CREATE ... WITH ... MATCH ... RETURN,
    and related pipeline/fallback shapes now execute instead of failing as
    generically unsupported, while malformed tails now surface deterministic
    invalid-query errors and correctly roll back implicit single-statement
    transactions. This reuses the existing multiple-create executor as a strict
    fallback after pipeline routing and adds regression coverage for the rollback
    case.
  • Cypher map keys containing : now parse correctly across map-literal
    surfaces.

    Quoted keys such as {'key:key': 'value'} were previously split at the
    first colon and mis-parsed in SET, MERGE, helper evaluators, APOC map
    parsing, and pipeline/map-literal call sites. Added a shared top-level
    key/value separator helper in pkg/cypher/pattern_parser.go and applied it
    across the affected map parsers, with parser-level and end-to-end regression
    coverage.
  • Bolt 4.x datetime/time encoding compatibility restored.
    pkg/bolt/packstream.go and pkg/bolt/server.go now negotiate older Bolt
    datetime encodings correctly, including the Rust-driver compatibility path
    and UTC/compatibility-sensitive record emission. Added focused packstream and
    server regressions for datetime structure decoding and negotiated time
    encoding.
  • Bound relationship delete correctness hardened.
    Unified delete projection typing so relationship delete targets are preserved
    as relationships rather than flattened into mismatched row shapes, and
    normalized dangling-edge traversal semantics so stale adjacency rows are
    skipped consistently instead of surfacing inconsistent delete behavior. This
    also adds focused delete-helper and chained-traversal regressions.
  • MVCC adjacency / visibility regressions corrected.
    Fixed several storage-layer correctness issues in the new snapshot-adjacency
    path, including edge visibility flags being dropped while copying materialized
    edges, pruning/order bugs that could tombstone visible adjacency incorrectly,
    and namespace filtering being applied too late during unprefixing.
  • Fulltext query parser: field:"value" AND (term) now intersects correctly.
    db.index.fulltext.queryNodes / queryRelationships previously tokenized the
    query with a whitespace splitter that had no notion of parenthesized groups,
    field-scoped clauses, or Lucene escape sequences. The Graphiti integration
    shape group_id:"g" AND (<terms>) silently discarded the parenthesized
    default-field clause — a real term and a nonsense term returned the identical
    result set, making the lexical arm of hybrid search term-blind. Replaced the
    ad-hoc tokenizer with a proper Lucene-classic recursive-descent parser
    (pkg/cypher/fulltext_query.go) plus per-document evaluator that supports
    the full grammar: boolean AND/OR/NOT with parens and nesting, +/-
    mandatory/prohibited clause prefixes, phrase queries and proximity
    ("a b"~n), fuzzy (term~n, Levenshtein), range queries ([a TO b],
    {a TO b} with mixed inclusivity), boost (^n), wildcards (?, *
    including leading and mid-token), regex (/re/), and full Lucene escape
    rules (\X decodes to literal X for any X — fixes the Cloud\Trail
    zero-hits case). Reference implementation: Neo4j's MultiFieldQueryParser
    with setAllowLeadingWildcard(true). Added decodeCypherStringLiteral in
    pkg/cypher/call_fulltext.go so backslash escapes survive round-trip
    through Cypher parameter substitution.
  • Post-WITH WHERE filtering and grouped aggregation restored.
    A cluster of Cypher WHERE evaluation issues against clean graph queries:
    • Split post-WITH WHERE from WITH projections so it is applied as a
      filter instead of being absorbed into the preceding alias.
    • Preserve grouped WITH aggregation semantics for OPTIONAL MATCH rows,
      including COUNT(c) over null optional targets.
    • Honor inline target labels/properties in relationship pattern predicates
      such as NOT (t)-[:R]->(:C {met:false}).
    • Evaluate bare boolean properties in traversal WHERE clauses so
      WHERE NOT c.met returns rows where c.met is false.
    • Allow multiple relationship CREATE clauses with inline endpoint nodes
      in one statement.
      Touched: pkg/cypher/clauses.go, create_pipeline_helpers.go,
      executor_mutations.go, match_rows.go, traversal.go plus new
      pkg/cypher/stats_query_test.go.
  • DDL / CALL-tail / UNWIND / typed conversion cluster fixes.
    • Preserve cardinality constraint parser errors for malformed
      REQUIRE MAX COUNT DDL.
    • Avoid compiling CALL-tail regex predicates using =~ as equality
      comparisons.
    • Fix post-UNWIND WHERE boundary parsing so equality/inequality filters
      apply correctly.
    • Prefer explicit typed assignment conversions before generic reflect
      conversion.
    • Deterministic coverage tests added for schema DDL, CALL-tail predicates,
      helper branches, UNWIND filtering, and typed result assignment
      (pkg/cypher/coverage_lift_test.go +1163 lines).

Performance

  • IN-list-anchored relationship traversal now index-seeds instead of
    scanning the whole label.

    MATCH (s:Label)-[rel]->() WHERE s.uid IN $list ... (with or without an
    additional AND predicate on the bound relationship, e.g.
    rel.evidence_source = $e) previously fell through every start-node
    pruning branch in executeMatchWithRelationshipsWithPath
    (pkg/cypher/traversal.go) — none of them recognized an IN [...] /
    IN $param predicate — straight to loadNodesWithTemporalViewport, an
    O(all nodes of the label) scan, even though the equivalent node-only
    MATCH (s:Label) WHERE s.uid IN $list RETURN s already used the schema
    property index via tryCollectNodesFromPropertyIndexIn(Literal)
    (pkg/cypher/match_index_seek.go, already wired into match.go,
    clauses.go, and executor_mutations.go). Added
    tryCollectNodesFromPropertyIndexInCompound, which wires those existing
    index-seek helpers into the traversal start-node pruning chain —
    including when the IN-list is one conjunct of an AND-combined WHERE
    clause, mirroring tryCollectNodesFromIDEqualityCompound's conjunct
    handling. Correctness is unaffected: filterPathsByWhere still
    re-evaluates the full WHERE clause after seeding, so pruning from one
    recognized conjunct can only over-fetch, never under-fetch.
    BenchmarkInListAnchoredRelMatch (50k-node label, 100-node target
    sublist; 5k was tried first but the algorithmic difference is inside
    measurement noise for an in-process MemoryEngine at that size) on this
    branch: 109,930,838 ns/op → 301,410 ns/op (~365x), 83,268,576 B/op →
    228,696 B/op (~364x), 1,388,688 allocs/op → 2,800 allocs/op (~496x).
    Added pkg/cypher/inlist_start_node_index_seed_bug_test.go
    (TestBug_InListStartNodeDoesNotIndexSeed + scan-budget and DELETE
    variants, TestTryCollectNodesFromPropertyIndexInCompound,
    BenchmarkInListAnchoredRelMatch).
  • Match/merge hot path from Graphify workloads.
    pkg/cypher/match_multi.go, merge.go, and pkg/storage/schema.go gain a
    fast pattern-property index lookup so multi-pattern MATCH/MERGE
    statements hit the schema index directly instead of scanning candidates.
    New benchmark (graphify_push_profile_bench_test.go) and
    pattern-property regression tests (match_pattern_property_index_test.go)
    guard the change.
  • Bound relationship deletes now use targeted source lookup and cheaper
    snapshot adjacency reads.

    The delete hot path now resolves bound relationship candidates using indexed
    and node-local transaction-snapshot reads instead of broader adjacency scans,
    reducing the cost of relationship delete workloads while keeping the delete
    projection contract intact.
  • Vulkan compute shaders tuned.
    Dispatch/binding refactor in pkg/gpu/vulkan/compute.go following the GPU
    HNSW build feature.

Changed

  • Dependency refresh — Go modules.
    • google.golang.org/api v0.286.0 → v0.287.0
    • google.golang.org/grpc v1.81.1 → v1.82.0
    • github.com/googleapis/enterprise-certificate-proxy v0.3.16 → v0.3.17
      (indirect)
    • google.golang.org/genproto/googleapis/rpc bumped to 20260622175928
      (indirect)
  • Dependency refresh — UI npm packages.
    • @ornery/ui-grid-core / -react / -vanilla ^1.0.6 → ^1.0.8
    • lucide-react ^1.22.0 → ^1.23.0
    • neo4j-driver ^6.1.0 → ^6.2.0
    • react-router-dom ^7.18.0 → ^7.18.1
    • three ^0.184.0 → ^0.185.0
  • Dependabot workflow versions refreshed across .github/workflows/
    (cd-llama-cpu.yml, cd-llama-cuda.yml, cd.yml, ci.yml,
    docs-pages.yml, release-macos.yml).
  • Documentation. README.md copy tweak. ORM/Neo4j-compatible streaming
    driver plan (plans/) expanded with implementation detail (+556/-189).

Tests

  • New pkg/cypher/coverage_lift_test.go (+1163 lines) exercising DDL,
    CALL-tail predicates, UNWIND filtering, and typed assignment.
  • New pkg/cypher/multi_match_relationship_binding_bug_test.go and
    pkg/cypher/inlist_start_node_index_seed_bug_test.go covering the
    multi-MATCH relationship-binding regression, the follow-up MATCH/WITH/MATCH
    pipeline behavior, and IN-list traversal index-seeding correctness,
    scan-budget, DELETE, and benchmark variants.
  • New pkg/cypher/kalman_functions_test.go covering the Kalman helper
    branches.
  • New pkg/cypher/fulltext_query_test.go and
    pkg/cypher/call_fulltext_parser_test.go covering the full Lucene-classic
    grammar surface plus e2e regressions against the Graphiti bug repro.
  • New pkg/cypher/stats_query_test.go, match_pattern_property_index_test.go,
    graphify_push_profile_bench_test.go guarding the WHERE-semantics and
    hot-path match/merge changes.
  • pkg/search/hnsw_build_gpu_test.go extended for GPU-build coverage.
  • New Bolt compatibility regressions in pkg/bolt/packstream_into_test.go and
    pkg/bolt/server_test.go covering older PackStream datetime structures,
    negotiated datetime emission, and Rust-driver compatibility.

What's Changed

  • feat(cypher,storage): adding adjacency snapshot isolation api by @orneryd in #253
  • Speed up bound relationship deletes by @linuxdynasty in #230
  • cover the bound relationship delete fast-path branches (follow-up to #230) by @linuxdynasty in #254
  • fix(cypher): bind relationships across multi-MATCH clauses; perf(cypher): index-seed IN-list traversal starts by @linuxdynasty in #259

Full Changelog: v1.1.10...v1.1.11