index

package
v0.1.0 Latest Latest
Warning

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

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

Documentation

Overview

Package index coordinates the secondary indexes attached to a labelled property graph.

A Manager owns a set of named indexes (label bitmap, hash exact-match, B+ tree range) and fans out mutations to every index that subscribes to the affected property or label. The fan-out is best-effort sequential: failures in one subscriber do not abort the others (subscribers are independent and idempotent).

Index

Examples

Constants

This section is empty.

Variables

View Source
var ErrIndexCorrupted = errors.New("index: serialized form corrupted")

ErrIndexCorrupted is returned by [Serializer.Deserialize] when the serialised form is structurally malformed or its CRC32C trailer does not match the payload. Callers (snapshot recovery in particular) treat this as "rebuild from the LPG" rather than as a fatal error.

View Source
var ErrIndexExists = errors.New("index: an index by that name already exists")

ErrIndexExists is returned by Manager.CreateIndex when the name is already in use.

View Source
var ErrIndexNotFound = errors.New("index: no index by that name")

ErrIndexNotFound is returned by Manager.DropIndex or Manager.GetIndex when the named index does not exist.

View Source
var ErrIndexValueTypeUnsupported = errors.New("index: value type not supported for serialization")

ErrIndexValueTypeUnsupported is returned by a generic index's Serialize / Deserialize methods when the value-type parameter is not in the supported on-disk encoding set (currently: string). Callers can convert their value type to string before registering the index for snapshot durability.

Functions

This section is empty.

Types

type Change

type Change struct {
	Op       ChangeOp
	Node     graph.NodeID
	Dst      graph.NodeID // edge changes only
	Property uint32       // 0 when not a property change
	Label    uint32       // 0 when not a label change

	// OldValue and NewValue are present only for property changes.
	// They are typed as any so this package stays generic across
	// every PropertyValue kind without importing the lpg package.
	OldValue any
	NewValue any
}

Change describes a single mutation observed by the Manager. Each subscriber inspects the relevant fields and decides whether to update its own state.

Property and Label fields are interned identifiers from the owning graph's registries (lpg.PropertyKeyID / lpg.LabelID), surfaced as uint32 so this package does not import the lpg package and create a cycle.

func (Change) IsEdgeChange

func (c Change) IsEdgeChange() bool

IsEdgeChange reports whether the change concerns an edge.

type ChangeOp

type ChangeOp uint8

ChangeOp tags the shape of a Change.

const (
	OpAddNodeLabel ChangeOp = iota + 1
	OpRemoveNodeLabel
	OpSetNodeProperty
	OpDelNodeProperty
	OpAddEdgeLabel
	OpRemoveEdgeLabel
	OpSetEdgeProperty
	OpDelEdgeProperty
)

Mutation kinds the Manager can fan out.

type Manager

type Manager struct {
	// contains filtered or unexported fields
}

Manager owns the set of named indexes attached to a graph and fans out mutations to every subscriber.

Manager is safe for concurrent use.

Example

ExampleManager shows the Manager lifecycle: register a concrete index under a name, list and count the registered indexes, and reject a duplicate registration with ErrIndexExists.

package main

import (
	"errors"
	"fmt"

	"github.com/FlavioCFOliveira/GoGraph/graph/index"
	"github.com/FlavioCFOliveira/GoGraph/graph/index/label"
)

func main() {
	m := index.NewManager()

	if err := m.CreateIndex("by_label", label.NewNodeIndex()); err != nil {
		fmt.Println("unexpected:", err)
	}

	// Re-registering the same name is rejected.
	err := m.CreateIndex("by_label", label.NewNodeIndex())
	fmt.Println("duplicate is ErrIndexExists:", errors.Is(err, index.ErrIndexExists))

	fmt.Println("count:", m.Count())
	fmt.Println("names:", m.ListIndexes())
}
Output:
duplicate is ErrIndexExists: true
count: 1
names: [by_label]

func NewManager

func NewManager() *Manager

NewManager returns an empty Manager.

func (*Manager) Apply

func (m *Manager) Apply(c Change)

Apply fans c out to every registered subscriber under a read lock so subscribers cannot be unregistered mid-update. The Manager itself does not enforce ordering across subscribers — each subscriber is expected to be order-independent on the change stream it observes.

Example

ExampleManager_Apply shows the Manager fanning a change out to every registered subscriber. The label index observes OpAddNodeLabel events and can then be queried back through GetIndex for the NodeIDs that carry a given label.

package main

import (
	"fmt"

	"github.com/FlavioCFOliveira/GoGraph/graph"
	"github.com/FlavioCFOliveira/GoGraph/graph/index"
	"github.com/FlavioCFOliveira/GoGraph/graph/index/label"
)

func main() {
	const labelPerson = uint32(7)

	m := index.NewManager()
	_ = m.CreateIndex("node_labels", label.NewNodeIndex())

	// A mutation observed by the owning graph is fanned out to every
	// subscriber. Here two nodes acquire the Person label.
	m.Apply(index.Change{Op: index.OpAddNodeLabel, Node: graph.NodeID(1), Label: labelPerson})
	m.Apply(index.Change{Op: index.OpAddNodeLabel, Node: graph.NodeID(4), Label: labelPerson})

	// Recover the concrete index to run a query.
	sub, _ := m.GetIndex("node_labels")
	idx := sub.(*label.Index)

	fmt.Println("kind:", idx.Kind())
	fmt.Println("Person count:", idx.Count(labelPerson))
	fmt.Println("Person members:", idx.Scan(labelPerson))
}
Output:
kind: label
Person count: 2
Person members: [1 4]

func (*Manager) ApplyBatch

func (m *Manager) ApplyBatch(changes []Change)

ApplyBatch fans an ordered slice of changes out to every subscriber in order. The whole batch is applied under one read lock; this is the substrate consumed by future transaction integration (Sprint 3).

func (*Manager) Count

func (m *Manager) Count() int

Count returns the number of currently registered indexes.

func (*Manager) CreateIndex

func (m *Manager) CreateIndex(name string, sub Subscriber) error

CreateIndex registers sub under name. Returns ErrIndexExists when the name is already taken.

func (*Manager) DropIndex

func (m *Manager) DropIndex(name string) error

DropIndex removes the named index.

func (*Manager) GetIndex

func (m *Manager) GetIndex(name string) (Subscriber, error)

GetIndex returns the subscriber registered under name.

func (*Manager) ListIndexes

func (m *Manager) ListIndexes() []string

ListIndexes returns the names of every currently registered index in unspecified order.

type Serializer

type Serializer interface {
	Serialize(w io.Writer) error
	Deserialize(r io.Reader) error
}

Serializer is implemented by indexes that can persist and restore their internal state through an io.Writer / io.Reader pair. The Manager type-asserts every registered Subscriber to this interface during snapshot writes; subscribers that do not implement Serializer are silently skipped (rebuild-on-restart).

Implementations must:

  • Write a fixed self-describing header (magic + format version) so a future format bump can be detected on read.
  • Cover the entire on-disk payload with a CRC32C trailer (uint32 little-endian) so corruption surfaces as ErrIndexCorrupted.
  • Be safe for concurrent reads from other goroutines while Serialize executes (typically by holding the index's own RLock for the duration of the write).

Deserialize replaces the receiver's state with the contents of r. On any structural problem or CRC mismatch the function returns a wrapped ErrIndexCorrupted and leaves the receiver in its previous state.

type Subscriber

type Subscriber interface {
	Apply(Change)
	// Kind returns a short stable identifier of the underlying index
	// implementation, used for introspection (e.g. "label", "hash",
	// "btree").
	Kind() string
}

Subscriber is implemented by every concrete index that wishes to receive change events from the Manager. The Apply method must be idempotent: replays of the same change must not produce duplicate state.

Directories

Path Synopsis
Package btree provides an order-preserving property index over a constraints.Ordered value type, answering range predicates against the NodeIDs that carry each value.
Package btree provides an order-preserving property index over a constraints.Ordered value type, answering range predicates against the NodeIDs that carry each value.
Package hash provides a sharded hash index from arbitrary comparable property values to the set of NodeIDs that carry them, represented as a 64-bit Roaring bitmap.
Package hash provides a sharded hash index from arbitrary comparable property values to the set of NodeIDs that carry them, represented as a 64-bit Roaring bitmap.
Package label provides a Roaring-bitmap-backed inverted index from label identifiers to the NodeIDs that carry them.
Package label provides a Roaring-bitmap-backed inverted index from label identifiers to the NodeIDs that carry them.

Jump to

Keyboard shortcuts

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