btree

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: 14 Imported by: 0

Documentation

Overview

Package btree provides an order-preserving property index over a constraints.Ordered value type, answering range predicates against the NodeIDs that carry each value.

The v1 implementation is a sorted-array index — sufficient to meet the Sprint 2 acceptance criteria (sub-microsecond Range first- result on a 10^7-element index, multi-million-entry bulk-load in seconds). The public API is designed so that a future replacement with a fan-out-tuned B+ tree (or skip list) can land without breaking callers; per-key Insert is O(n) today and should be avoided on the hot path in favour of Index.BulkLoad.

All operations are safe for concurrent use; a single sync.RWMutex guards the array.

Index

Examples

Constants

This section is empty.

Variables

View Source
var ErrMismatchedLengths = errors.New("btree: values and nodes slices must have the same length")

ErrMismatchedLengths is returned by Index.BulkLoad when the values and nodes slices supplied to it do not share a common length. Before sprint 21 this condition panicked; the error returned here lets callers handle it as a recoverable input validation failure.

Functions

This section is empty.

Types

type Index

type Index[V cmp.Ordered] struct {
	// contains filtered or unexported fields
}

Index is an order-preserving property index keyed by V.

Example

ExampleIndex shows an order-preserving property index keyed by an ordered value type: insert (value, NodeID) pairs, then read back the NodeIDs carrying one exact value and the half-open count of distinct values.

package main

import (
	"fmt"

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

func main() {
	// An index over an integer "age" property.
	idx := btree.New[int]()
	idx.Insert(30, graph.NodeID(1))
	idx.Insert(25, graph.NodeID(2))
	idx.Insert(30, graph.NodeID(3)) // same value, different node

	// Lookup returns the NodeID set carrying exactly age == 30.
	bm := idx.Lookup(30)
	fmt.Println("age==30 nodes:", bm.ToArray())
	fmt.Println("age==30 cardinality:", idx.Cardinality(30))
	fmt.Println("distinct ages:", idx.DistinctValues())
}
Output:
age==30 nodes: [1 3]
age==30 cardinality: 2
distinct ages: 2

func New

func New[V cmp.Ordered]() *Index[V]

New returns an empty index.

func (*Index[V]) Apply

func (*Index[V]) Apply(index.Change)

Apply is a no-op for the generic B+ tree index. See the matching note on hash.Index.Apply — the index cannot auto-project arbitrary index.Change values without caller-supplied bindings.

func (*Index[V]) BulkLoad

func (i *Index[V]) BulkLoad(values []V, nodes []graph.NodeID) error

BulkLoad replaces the contents of the index with the given (value, node) pairs in O(n log n) time. The pairs slice is left untouched. Calling BulkLoad on a non-empty index discards previous data. Returns ErrMismatchedLengths when len(values) != len(nodes).

func (*Index[V]) Cardinality

func (i *Index[V]) Cardinality(value V) uint64

Cardinality returns the number of NodeIDs associated with value.

func (*Index[V]) Delete

func (i *Index[V]) Delete(value V, node graph.NodeID)

Delete removes node from the set associated with value. No-op when absent. The (value, bitmap) entry is removed when its bitmap becomes empty.

func (*Index[V]) Deserialize

func (i *Index[V]) Deserialize(r io.Reader) error

Deserialize replaces the receiver's state with the contents of r. Because the writer dumps entries in ascending key order, the reader can build the sorted entries slice directly without an extra sort pass; the loader is therefore O(n) instead of Index.BulkLoad's O(n log n).

func (*Index[V]) DistinctValues

func (i *Index[V]) DistinctValues() int

DistinctValues returns the number of distinct values currently indexed.

func (*Index[V]) Insert

func (i *Index[V]) Insert(value V, node graph.NodeID)

Insert records that node carries value.

func (*Index[V]) Kind

func (*Index[V]) Kind() string

Kind returns "btree" — satisfies index.Subscriber.

func (*Index[V]) Lookup

func (i *Index[V]) Lookup(value V) *roaring64.Bitmap

Lookup returns a clone of the bitmap associated with value, or an empty bitmap when value is unknown.

func (*Index[V]) Range

func (i *Index[V]) Range(lo, hi V) *roaring64.Bitmap

Range returns a Roaring bitmap that is the union of the per-value bitmaps for every key v with lo <= v <= hi. The returned bitmap is freshly allocated; the caller owns it.

Example

ExampleIndex_Range shows an inclusive range predicate [lo, hi]: Range returns every NodeID whose value satisfies lo <= v <= hi, and RangeFirst returns the smallest matching value together with one of its NodeIDs (the order-preserving property the index exists for).

package main

import (
	"fmt"

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

func main() {
	idx := btree.New[int]()
	if err := idx.BulkLoad(
		[]int{10, 20, 30, 40},
		[]graph.NodeID{1, 2, 3, 4},
	); err != nil {
		fmt.Println("bulk load:", err)
		return
	}

	// Range is inclusive on both ends: [20, 40] selects 20, 30 and 40.
	bm := idx.Range(20, 40)
	fmt.Println("nodes in [20,40]:", bm.ToArray())

	v, node, ok := idx.RangeFirst(20, 40)
	fmt.Printf("first in [20,40]: value=%d node=%d ok=%t\n", v, node, ok)
}
Output:
nodes in [20,40]: [2 3 4]
first in [20,40]: value=20 node=2 ok=true

func (*Index[V]) RangeFirst

func (i *Index[V]) RangeFirst(lo, hi V) (V, graph.NodeID, bool)

RangeFirst returns the first NodeID in the smallest indexed value not less than lo and not greater than hi, plus that value. The second return value reports whether any match exists. It is the allocation-free way to peek the first row of a range scan; the full union of matches is available via Index.Range.

func (*Index[V]) Serialize

func (i *Index[V]) Serialize(w io.Writer) error

Serialize writes every (value, NodeID-set) pair in key order to w. The on-disk layout is:

uint32 magic ('SBTR')
uint32 formatVersion
uint64 entryCount
repeat entryCount times:
  uint32 keyLen
  [keyLen]byte key (kind-specific encoding)
  uint64 idCount
  [idCount]uint64 NodeIDs (sorted ascending)
uint32 crc32c (little-endian)

Writing in key order lets [Deserialize] use Index.BulkLoad indirectly: the reader appends one entry at a time and the sorted order is preserved.

Jump to

Keyboard shortcuts

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