Skip to content

Add EdgesBetween and viz.FromQuery, support IN filters, revise docs and CI - #4

Merged
justintout merged 9 commits into
mainfrom
feature/edges-viz-query
Oct 10, 2026
Merged

justintout merged 9 commits into
mainfrom
feature/edges-viz-query

Conversation

@justintout

Copy link
Copy Markdown
Owner

Summary

  • Query API:
    • IN and NOT IN filters now work and take a slice. They used to compile to x IN ?, which SQLite rejects. The slice is sent as one JSON array read with json_each, so the SQL text stays the same whatever the list length.
    • Return("labels") and Query.WithLabels() fill in Node.Labels. Labels stay off by default: returning them by default made traversals 30–50% slower.
    • EdgesBetween(ctx, nodeIDs, types...), on both Graph and Tx, returns the edges between a set of nodes. A unary + on target_id makes SQLite seek by source; without it, the plan did one index seek per source and target pair.
  • viz:
    • FromQuery(ctx, db, q, options...) replaces NewFromResult. It runs the query with labels and draws every edge between the result's nodes.
    • Chart nodes now carry their graph ID. Echarts falls back to node names, so two nodes with the same name used to merge into one.
  • Docs:
    • README updated for accuracy (dependencies, hop cap, pool options, update and transaction behavior, what results contain) and edited for style.
    • Benchmark results moved to BENCHMARKS.md.
  • CI and tooling:
    • The test workflow now checks gofmt, vets the bench/compare module, and uses current action versions.
    • bench/compare uses a root go.work instead of a replace directive.
    • Added a // Package graph doc comment.

Testing

  • Tests:
    • go vet ./... ./bench/compare/... and go test -race ./... pass.
    • New: TestWhereIn, TestEdgesBetween, TestFromQuery, TestRenderDuplicateNames.
    • TestReturn now covers labels.
  • Query plans: checked with EXPLAIN QUERY PLAN for the labels subquery and EdgesBetween.
  • Benchmarks: labels-by-default was compared against the base commit with benchstat, 6 runs each. That comparison is why labels are opt-in. Default queries compile to the same SQL as before.

🤖 Generated with Claude Code

https://claude.ai/code/session_01YB9owLYxU6NsNuVk2NVc7C

justintout and others added 9 commits September 14, 2026 14:11
Document PoolSize, WAL, the viz subpackage, UpdateNode label replacement,
partial writes inside a Tx, query result contents, supported operators,
and BreadthFirst trade-offs. Remove claims that the hop cap is
configurable and that the module has one dependency. Add Return and
BreadthFirst benchmark results.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YB9owLYxU6NsNuVk2NVc7C
Both operators were allowed but compiled to "x IN ?", which SQLite
rejects. They now take a slice, bound as one JSON array and read with
json_each, so the SQL text does not change with the list length and the
prepared statement cache stays bounded. A property filter casts by the
slice element type. A non-slice value is a builder error.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YB9owLYxU6NsNuVk2NVc7C
The test workflow now fails on unformatted files and vets bench/compare,
and uses current checkout and setup-go actions.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YB9owLYxU6NsNuVk2NVc7C
Echarts identifies graph nodes by id, then name. Without an id it kept
only the first node with a given name and attached the edges of the
others to it. Nodes now carry their graph ID, and edges link by it.
Also gofmt convert.go.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YB9owLYxU6NsNuVk2NVc7C
Query results never set Node.Labels, so viz.NewFromResult put every node
in one color category. Labels are opt-in: reading them runs a subquery
and decodes a JSON array per node, which made default traversals 30-50%
slower. The subquery reads node_labels in primary key order; aggregate
ORDER BY built a temp b-tree per node and was slower still.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YB9owLYxU6NsNuVk2NVc7C
EdgesBetween returns the edges whose endpoints are both in a set of
node IDs, optionally filtered by type. It is the only way to read edges
for a query result without raw SQL. The ID and type lists bind as JSON
arrays, and a unary + keeps the planner seeking by source instead of
once per source and target pair.

WithLabels adds labels to whatever fields a query returns, where
Return("labels") replaces them.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YB9owLYxU6NsNuVk2NVc7C
NewFromResult needed labels the query had not selected and edges the
caller had no API to read. FromQuery runs the query with labels and
reads the edges between its nodes. New remains for choosing nodes and
edges directly.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YB9owLYxU6NsNuVk2NVc7C
@justintout
justintout merged commit fc90376 into main Oct 10, 2026
1 check passed
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