query

package
v0.3.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 15, 2026 License: MIT Imports: 5 Imported by: 0

Documentation

Overview

Package query provides a fluent, type-safe programmatic API for expressing MATCH-style pattern queries against a labelled property graph snapshot.

The API is intentionally minimal in v1: it covers the high-value "MATCH (n:Label1) WHERE n.prop = v RETURN n" pattern and its single-hop extension "(:Label1)-[]->(:Label2)". Multi-hop chains compose via repeated Pattern.Out / [Pattern.Filter] calls; the engine transparently uses the lpg.Graph's NodeIndex (Roaring bitmaps) when a WithLabel predicate seeds the pattern.

A future iteration will plug in graph/index.Manager so the planner can choose between hash, btree, and full-scan plans based on cardinality estimates.

Concurrency

An Engine is read-only and takes no lock across pattern steps, so it is safe for concurrent use by multiple goroutines only while the underlying lpg.Graph and CSR snapshot are quiescent (no concurrent mutation); this is the same quiescence the CSR snapshot already requires. A Pattern is a single MATCH expression under construction: it is mutated in place by each builder call (Pattern.Vertex, Pattern.Out) and is NOT safe for concurrent use — a Pattern is owned by one goroutine.

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Engine

type Engine[N comparable, W any] struct {
	// contains filtered or unexported fields
}

Engine bundles an lpg.Graph with its CSR snapshot for read-only query execution. The CSR is used for adjacency traversal; the LPG is used for label / property lookups.

Removed nodes are invisible: every working-set construction step (seeding, label intersection, Pattern.Out expansion) prunes NodeIDs that lpg.Graph.IsTombstoned reports as removed, so Pattern.Cardinality, Pattern.Collect, and Pattern.NodeIDs never observe deleted state. Each pattern step reads the tombstone set at the moment it executes; the Engine takes no lock across steps, so callers that mutate the graph concurrently with query construction must serialise externally (the same quiescence the CSR snapshot already requires).

func New

func New[N comparable, W any](g *lpg.Graph[N, W], c *csr.CSR[W]) *Engine[N, W]

New returns an Engine wrapping g and the CSR snapshot c.

func (*Engine[N, W]) Match

func (e *Engine[N, W]) Match() *Pattern[N, W]

Match opens a new MATCH expression seeded with every live node in the graph: interned NodeIDs only (never the ghost slots the sharded id packing leaves in [0, MaxNodeID)), minus the tombstoned set.

Example

ExampleEngine_Match expresses "MATCH (n:Person) RETURN n" with the fluent pattern API. The label predicate seeds the working set from the graph's label index; Cardinality reports its size and Collect returns the matching user keys (order unspecified, sorted here).

package main

import (
	"fmt"
	"sort"

	"github.com/FlavioCFOliveira/GoGraph/graph/adjlist"
	"github.com/FlavioCFOliveira/GoGraph/graph/csr"
	"github.com/FlavioCFOliveira/GoGraph/graph/lpg"
	"github.com/FlavioCFOliveira/GoGraph/graph/query"
)

// buildSocialGraph returns a tiny labelled property graph and its CSR
// snapshot: two :Person nodes (alice, bob) who both BOUGHT a :Product
// (widget).
func buildSocialGraph() (*lpg.Graph[string, int], *csr.CSR[int]) {
	g := lpg.New[string, int](adjlist.Config{Directed: true})
	for _, p := range []string{"alice", "bob"} {
		_ = g.AddNode(p)
		_ = g.SetNodeLabel(p, "Person")
		_ = g.SetNodeProperty(p, "name", lpg.StringValue(p))
	}
	_ = g.AddNode("widget")
	_ = g.SetNodeLabel("widget", "Product")
	_ = g.AddEdge("alice", "widget", 0)
	_ = g.AddEdge("bob", "widget", 0)
	return g, csr.BuildFromAdjList(g.AdjList())
}

func main() {
	g, snap := buildSocialGraph()
	eng := query.New(g, snap)

	people := eng.Match().Vertex(query.WithLabel[string, int]("Person"))

	keys := people.Collect()
	sort.Strings(keys)
	fmt.Println("count:", people.Cardinality())
	fmt.Println("people:", keys)
}
Output:
count: 2
people: [alice bob]

type Pattern

type Pattern[N comparable, W any] struct {
	// contains filtered or unexported fields
}

Pattern is a single MATCH expression under construction. Its working set (the current NodeID bitmap) is mutated in place by each builder call (Pattern.Vertex, Pattern.Out), so a Pattern is NOT safe for concurrent use: a Pattern is owned by one goroutine.

func (*Pattern[N, W]) Cardinality

func (p *Pattern[N, W]) Cardinality() uint64

Cardinality returns the size of the current working set.

func (*Pattern[N, W]) Collect

func (p *Pattern[N, W]) Collect() []N

Collect returns the user-facing N values in the working set.

func (*Pattern[N, W]) NodeIDs

func (p *Pattern[N, W]) NodeIDs() iter.Seq[graph.NodeID]

NodeIDs returns an iterator over the NodeIDs in the working set.

func (*Pattern[N, W]) Out

func (p *Pattern[N, W]) Out() *Pattern[N, W]

Out expands the working set to the out-neighbours of every node in it. Neighbours that have been tombstoned since the CSR snapshot was built are pruned: the snapshot still stores their incident edges, but a removed node must never re-enter the working set.

Example

ExamplePattern_Out follows out-edges one hop — "MATCH (:Person)-[]->(p) RETURN p" — collapsing both buyers onto the single product they bought.

package main

import (
	"fmt"

	"github.com/FlavioCFOliveira/GoGraph/graph/adjlist"
	"github.com/FlavioCFOliveira/GoGraph/graph/csr"
	"github.com/FlavioCFOliveira/GoGraph/graph/lpg"
	"github.com/FlavioCFOliveira/GoGraph/graph/query"
)

// buildSocialGraph returns a tiny labelled property graph and its CSR
// snapshot: two :Person nodes (alice, bob) who both BOUGHT a :Product
// (widget).
func buildSocialGraph() (*lpg.Graph[string, int], *csr.CSR[int]) {
	g := lpg.New[string, int](adjlist.Config{Directed: true})
	for _, p := range []string{"alice", "bob"} {
		_ = g.AddNode(p)
		_ = g.SetNodeLabel(p, "Person")
		_ = g.SetNodeProperty(p, "name", lpg.StringValue(p))
	}
	_ = g.AddNode("widget")
	_ = g.SetNodeLabel("widget", "Product")
	_ = g.AddEdge("alice", "widget", 0)
	_ = g.AddEdge("bob", "widget", 0)
	return g, csr.BuildFromAdjList(g.AdjList())
}

func main() {
	g, snap := buildSocialGraph()
	eng := query.New(g, snap)

	bought := eng.Match().
		Vertex(query.WithLabel[string, int]("Person")).
		Out()

	fmt.Println("count:", bought.Cardinality())
	fmt.Println("products:", bought.Collect())
}
Output:
count: 1
products: [widget]

func (*Pattern[N, W]) Vertex

func (p *Pattern[N, W]) Vertex(preds ...Predicate[N, W]) *Pattern[N, W]

Vertex constrains the working set by the conjunction of preds. The first call with a WithLabel predicate uses the LPG's label index (Roaring intersect) for the planner's fast path; subsequent calls fall back to a per-node scan.

type Predicate

type Predicate[N comparable, W any] interface {
	Match(g *lpg.Graph[N, W], id graph.NodeID) bool
}

Predicate is the type-safe interface a Vertex constraint implements. Implementations may consult the lpg.Graph freely; returning true keeps the NodeID in the working set.

func WithLabel

func WithLabel[N comparable, W any](name string) Predicate[N, W]

WithLabel returns a Predicate selecting nodes carrying the given label.

func WithProperty

func WithProperty[N comparable, W any](key string, expected lpg.PropertyValue) Predicate[N, W]

WithProperty returns a Predicate selecting nodes whose named property matches the given expected value (kind and value-equality both required).

Example

ExampleWithProperty filters a pattern by an exact property match — "MATCH (n) WHERE n.name = 'alice' RETURN n".

package main

import (
	"fmt"

	"github.com/FlavioCFOliveira/GoGraph/graph/adjlist"
	"github.com/FlavioCFOliveira/GoGraph/graph/csr"
	"github.com/FlavioCFOliveira/GoGraph/graph/lpg"
	"github.com/FlavioCFOliveira/GoGraph/graph/query"
)

// buildSocialGraph returns a tiny labelled property graph and its CSR
// snapshot: two :Person nodes (alice, bob) who both BOUGHT a :Product
// (widget).
func buildSocialGraph() (*lpg.Graph[string, int], *csr.CSR[int]) {
	g := lpg.New[string, int](adjlist.Config{Directed: true})
	for _, p := range []string{"alice", "bob"} {
		_ = g.AddNode(p)
		_ = g.SetNodeLabel(p, "Person")
		_ = g.SetNodeProperty(p, "name", lpg.StringValue(p))
	}
	_ = g.AddNode("widget")
	_ = g.SetNodeLabel("widget", "Product")
	_ = g.AddEdge("alice", "widget", 0)
	_ = g.AddEdge("bob", "widget", 0)
	return g, csr.BuildFromAdjList(g.AdjList())
}

func main() {
	g, snap := buildSocialGraph()
	eng := query.New(g, snap)

	match := eng.Match().Vertex(
		query.WithProperty[string, int]("name", lpg.StringValue("alice")),
	)

	fmt.Println("count:", match.Cardinality())
	fmt.Println("keys:", match.Collect())
}
Output:
count: 1
keys: [alice]

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL