Documentation
¶
Index ¶
- Variables
- type Algorithm
- type AlgorithmResult
- type BFS
- type Direction
- type Entity
- type ExcessTraversal
- type Fact
- type Graph
- type GraphStats
- type InMemoryGraph
- func (g *InMemoryGraph[K, P]) AdjacencyMap() map[K]map[K]K
- func (g *InMemoryGraph[K, P]) Copy() Graph[K, P]
- func (g *InMemoryGraph[K, P]) Delete(node Node[K]) error
- func (g *InMemoryGraph[K, P]) Get(key K) Node[K]
- func (g *InMemoryGraph[K, P]) GetHasher() hash.Hasher[K, string]
- func (g *InMemoryGraph[K, P]) GetTextIndex() index.TextIndex[K, P]
- func (g *InMemoryGraph[K, P]) GetVectorIndex() index.VectorIndex[K, P]
- func (g *InMemoryGraph[K, P]) Lock()
- func (g *InMemoryGraph[K, P]) MergeFrom(in Graph[K, P])
- func (g *InMemoryGraph[K, P]) Neighbours(key K) []K
- func (g *InMemoryGraph[K, P]) Nodes() map[K]Node[K]
- func (g *InMemoryGraph[K, P]) Order() int
- func (g *InMemoryGraph[K, P]) PredecessorMap() map[K]map[K]K
- func (g *InMemoryGraph[K, P]) Put(key K, node Node[K]) error
- func (g *InMemoryGraph[K, P]) RLock()
- func (g *InMemoryGraph[K, P]) RUnlock()
- func (g *InMemoryGraph[K, P]) Search(keywords []string, vector containers.Vector[K, P], topics []string, ...) ([]*Node[K], []P, [][]scoring.Contribution[K, P], P)
- func (g *InMemoryGraph[K, P]) Set(node Node[K]) error
- func (g *InMemoryGraph[K, P]) SetRanking(r Ranking[K, P])
- func (g *InMemoryGraph[K, P]) SetScorer(s scoring.Scorer[K, P])
- func (g *InMemoryGraph[K, P]) SetTraversal(t Traversal[K, P])
- func (g *InMemoryGraph[K, P]) Size() int
- func (g *InMemoryGraph[K, P]) Stats() GraphStats
- func (g *InMemoryGraph[K, P]) Unlock()
- type IsAbout
- type Mentions
- func (m Mentions[K]) GetAttributes() *NodeAttributes
- func (m Mentions[K]) GetTimestamp() time.Time
- func (m Mentions[K]) GetValue() string
- func (m Mentions[K]) Hash(h hash.Hasher[K, string]) K
- func (m Mentions[K]) Key() K
- func (m Mentions[K]) Source() *Entity[K]
- func (m Mentions[K]) Target() *Entity[K]
- type NamedEntity
- type Node
- type NodeAttributes
- type PageRank
- type Ranking
- type RankingResult
- type Relationship
- type Topic
- type Traversal
- type TraversalResult
Constants ¶
This section is empty.
Variables ¶
var ( // ErrNilNode is returned when a nil node is passed to a graph mutation. ErrNilNode = errors.New("graph: node is nil") // ErrNodeAlreadyExists is returned when adding a node whose key is already // present in the graph. ErrNodeAlreadyExists = errors.New("graph: node already exists") // ErrNodeNotFound is returned when an operation references a node that is // not in the graph. ErrNodeNotFound = errors.New("graph: node not found") // ErrSourceNotFound is returned by traversal algorithms (e.g. BFS) when the // requested source vertex is absent from the graph. ErrSourceNotFound = errors.New("algorithms: source vertex not found in graph") // ErrEmptyGraph is returned by ranking algorithms (e.g. PageRank) when the // graph has no edges to rank over. ErrEmptyGraph = errors.New("algorithms: graph has no edges to rank") )
Functions ¶
This section is empty.
Types ¶
type Algorithm ¶
type Algorithm[K comparable, P float32 | float64] interface { Run(g Graph[K, P]) (AlgorithmResult, error) }
Algorithm is the common root of every graph algorithm. It exposes only identity; the runnable contract lives on the Traversal and Ranking sub-interfaces, which differ in what they consume and produce.
type AlgorithmResult ¶
type AlgorithmResult interface {
}
type BFS ¶
type BFS[K comparable, P float32 | float64] struct { // contains filtered or unexported fields }
BFS is a breadth-first traversal: it explores the graph frontier by frontier (nearest vertices first) using a FIFO queue. It implements Traversal; Run starts from the source configured at construction, while Traverse takes the source as a parameter so one BFS value can serve many seeds.
func NewBFS ¶
func NewBFS[K comparable, P float32 | float64](source K, dir Direction) *BFS[K, P]
NewBFS returns a breadth-first traversal that starts from source and follows edges in the given direction.
func NewBFSTraversal ¶
func NewBFSTraversal[K comparable, P float32 | float64](dir Direction) *BFS[K, P]
NewBFSTraversal returns a breadth-first Traversal following edges in the given direction, for use as a graph's configured search traversal (where the source is supplied per Traverse call).
func (*BFS[K, P]) Clone ¶
Clone returns a new BFS with the same direction and no source set, so it can be given its own source and run independently of the original. This lets a single configured traversal serve many concurrent walks without sharing the mutable source.
type Direction ¶
type Direction int
Direction selects which edges a traversal follows in a directed graph.
type Entity ¶
type Entity[K comparable] interface { Node[K] GetValue() string }
type ExcessTraversal ¶
type ExcessTraversal[K cmp.Ordered, P float32 | float64] struct { // contains filtered or unexported fields }
ExcessTraversal is the excess methodology's traversal: from a source fact it visits the source's anchors — Topic and NamedEntity neighbours, both edge directions — at depth 1, then every member of those anchors at depth 2. Order lists the anchors then the members, ascending key within each band, so the collection layer's float folds run in a fixed order. Parents carries the full member→anchors incidence (a member reached through two of the source's anchors is two observations, not one), and the source itself appears among its anchors' members: observed anchor mass then aggregates identically for every observer, and self-exclusion happens once, in the scorer, where the member's own mass is at hand. K is ordered, not merely comparable: the band ordering is what lets the collection layer fold floats without run-to-run drift, and an unordered key type would have no order to band by.
func NewExcessTraversal ¶
func NewExcessTraversal[K cmp.Ordered, P float32 | float64]() *ExcessTraversal[K, P]
NewExcessTraversal returns the excess-transmission traversal.
func (*ExcessTraversal[K, P]) Clone ¶
func (t *ExcessTraversal[K, P]) Clone() Traversal[K, P]
Clone returns a fresh traversal with no per-run state, so each walk can set its own source instead of mutating a shared instance.
func (*ExcessTraversal[K, P]) Run ¶
func (t *ExcessTraversal[K, P]) Run(g Graph[K, P]) (AlgorithmResult, error)
Run traverses from the configured source, returning an AlgorithmResult (a TraversalResult).
func (*ExcessTraversal[K, P]) SetSource ¶
func (t *ExcessTraversal[K, P]) SetSource(source K)
SetSource configures the vertex the next Run starts from.
type Fact ¶
type Fact[K comparable] struct { NodeAttributes Hasher hash.Hasher[K, string] `json:"-"` }
func (Fact[K]) GetAttributes ¶
func (f Fact[K]) GetAttributes() *NodeAttributes
func (Fact[K]) GetTimestamp ¶
type Graph ¶
type Graph[K comparable, P float32 | float64] interface { GetHasher() hash.Hasher[K, string] // Get returns the node stored under key, or nil if absent. Get(key K) Node[K] // Set inserts a new node, deriving its key via the graph's hash // function. Set(node Node[K]) error // Put replaces the node stored under key with the given node. Put(key K, node Node[K]) error // Delete removes the node (and, by extension, its index entries and // incident relationships). Delete(node Node[K]) error // GetVectorIndex returns the graph's vector (semantic) index, keyed // by node key and storing embedding vectors of precision P. GetVectorIndex() index.VectorIndex[K, P] // GetTextIndex returns the graph's full-text search index. GetTextIndex() index.TextIndex[K, P] // MergeFrom merges the contents of g into this graph: nodes, // relationships and index entries. Nodes with colliding keys are // resolved by the implementation. MergeFrom(g Graph[K, P]) // Copy returns a deep copy of the graph, independent of the // original: mutating one never affects the other. Copy() Graph[K, P] Nodes() map[K]Node[K] // AdjacencyMap returns the outgoing-edge view of the graph: // AdjacencyMap()[from][to] is the relationship from -> to. AdjacencyMap() map[K]map[K]K // PredecessorMap returns the incoming-edge view of the graph: // PredecessorMap()[to][from] is the relationship from -> to. It is // the transpose of AdjacencyMap and serves reverse traversal. PredecessorMap() map[K]map[K]K // Neighbours returns the keys adjacent to key in either direction // (successors and predecessors), in unspecified order. Unlike // AdjacencyMap/PredecessorMap it copies no more than one node's edge // rows, so a source-rooted traversal can read a single node's // neighbourhood without cloning the entire edge set on every call. Neighbours(key K) []K // Order returns the number of entities (vertices) in the graph. Order() int // Size returns the number of relationships (edges) in the graph. Size() int // Stats returns a point-in-time snapshot of the graph's shape. Stats() GraphStats // Search runs a hybrid query over the graph and returns matching // nodes alongside their ranking scores and the contribution records // the scores were folded from (parallel slices, ordered best-first, // at most top entries), plus the query's background rate — the one // query-global observation the scoring fold used, which explain // serializes so a client can recompute every score from its payload. // A caller that only wants ranked hits discards both. // // All criteria are optional and combine to narrow the result: // - keywords: full-text terms matched against the text index // - vector: query embedding for nearest-neighbor search; nil // (or empty) skips the vector index // - topics: restrict results to facts tagged with these topics // - entities: restrict results to facts involving these entities // - depth: selects the retrieval lane. 0 skips the anchor // traversal and ranks by seed mass alone (the floor; the // fast, text-only lane). 1 and 2 both run the one // anchor-mediated round and differ only in how much // above-chance evidence an anchor needs to transmit: 1 is // the precision lane, 2 admits at the plain fair share for // maximum recall. It does not iterate // - top: maximum number of results returned // - since: inclusive lower time bound; zero value = unbounded // - until: exclusive upper time bound; zero value = unbounded Search(keywords []string, vector containers.Vector[K, P], topics []string, entities []string, depth int, top int, since time.Time, until time.Time) ([]*Node[K], []P, [][]scoring.Contribution[K, P], P) // RLock acquires the lock for reading. RLock() // Lock acquires the lock for writing. Lock() // RUnlock releases a read lock. RUnlock() // Unlock releases a write lock. Unlock() }
Graph is a temporal memory graph: the storage atomic component of the database server. A la Redis, every database server holds multiple in-memory graphs addressed by index (see the '@n' graph selector in FQL).
K is the node key type; P is the floating-point precision used for embedding vectors and ranking scores.
Implementations are guarded by the embedded read-write lock methods; callers are responsible for acquiring the appropriate lock around the operations they compose (see the locking section below).
type GraphStats ¶
type GraphStats struct {
Order int `json:"order"` // number of entities (vertices)
Size int `json:"size"` // number of relationships (edges)
Nodes int `json:"nodes"` // total stored nodes
Vectors int `json:"vectors"` // total vectors indexed
// ForestEntries is how many entries the vector forest holds (live vectors
// plus garbage awaiting compaction); bounded by the index's flush factor
// times Vectors. 0 for index implementations without a forest.
ForestEntries int `json:"forest_entries"`
}
GraphStats is a point-in-time snapshot of a graph's shape.
type InMemoryGraph ¶
type InMemoryGraph[K ~uint64, P float32 | float64] struct { // contains filtered or unexported fields }
InMemoryGraph is the in-process implementation of Graph. Nodes live in a key-addressed map, relationships in a pair of mirrored adjacency maps, and two secondary indices serve hybrid search: a BTree full-text index over the nodes' values and an RPTree (random projection forest) vector index over caller-provided embeddings.
func (*InMemoryGraph[K, P]) AdjacencyMap ¶
func (g *InMemoryGraph[K, P]) AdjacencyMap() map[K]map[K]K
func (*InMemoryGraph[K, P]) Copy ¶
func (g *InMemoryGraph[K, P]) Copy() Graph[K, P]
Copy returns a deep copy of the graph: nodes, relationships and both indices are rebuilt so mutating one graph never affects the other.
func (*InMemoryGraph[K, P]) Delete ¶
func (g *InMemoryGraph[K, P]) Delete(node Node[K]) error
Delete removes the node, its incident relationships and its index entries. Whichever end of an edge is deleted, the edge leaves as a whole — its node and both adjacency entries — because the two halves are one fact about the graph: a Mentions left in idToNodes describes an edge that no longer exists (Nodes and Stats keep reporting it, and it keeps its text-index entry), while an adjacency entry left behind names a relationship node that is no longer stored, so Size counts an edge AdjacencyMap cannot resolve.
func (*InMemoryGraph[K, P]) Get ¶
func (g *InMemoryGraph[K, P]) Get(key K) Node[K]
Get returns the node stored under key, or nil if absent.
func (*InMemoryGraph[K, P]) GetHasher ¶
func (g *InMemoryGraph[K, P]) GetHasher() hash.Hasher[K, string]
Get hasher
func (*InMemoryGraph[K, P]) GetTextIndex ¶
func (g *InMemoryGraph[K, P]) GetTextIndex() index.TextIndex[K, P]
Returns the graph full text search index
func (*InMemoryGraph[K, P]) GetVectorIndex ¶
func (g *InMemoryGraph[K, P]) GetVectorIndex() index.VectorIndex[K, P]
Returns the graph vector index
func (*InMemoryGraph[K, P]) MergeFrom ¶
func (g *InMemoryGraph[K, P]) MergeFrom(in Graph[K, P])
MergeFrom merges the contents of in into this graph: nodes, relationships and index entries. On key collision the incoming node wins.
func (*InMemoryGraph[K, P]) Neighbours ¶
func (g *InMemoryGraph[K, P]) Neighbours(key K) []K
Neighbours returns the keys adjacent to key in either direction without copying the whole edge set: it allocates one slice sized to that node's own degree. This is the read a source-rooted traversal needs — the per-seed ExcessTraversal uses it instead of AdjacencyMap/PredecessorMap, which each deep-copy every edge in the graph on every call.
func (*InMemoryGraph[K, P]) Nodes ¶
func (g *InMemoryGraph[K, P]) Nodes() map[K]Node[K]
func (*InMemoryGraph[K, P]) Order ¶
func (g *InMemoryGraph[K, P]) Order() int
Order returns the number of entities (vertices) in the graph.
func (*InMemoryGraph[K, P]) PredecessorMap ¶
func (g *InMemoryGraph[K, P]) PredecessorMap() map[K]map[K]K
func (*InMemoryGraph[K, P]) Put ¶
func (g *InMemoryGraph[K, P]) Put(key K, node Node[K]) error
Put stores node under key, replacing whatever was there.
func (*InMemoryGraph[K, P]) Search ¶
func (g *InMemoryGraph[K, P]) Search(keywords []string, vector containers.Vector[K, P], topics []string, entities []string, depth int, top int, since time.Time, until time.Time) ([]*Node[K], []P, [][]scoring.Contribution[K, P], P)
func (*InMemoryGraph[K, P]) Set ¶
func (g *InMemoryGraph[K, P]) Set(node Node[K]) error
Set inserts a new node under its own ID, returning ErrNodeAlreadyExists if the key is taken.
func (*InMemoryGraph[K, P]) SetRanking ¶
func (g *InMemoryGraph[K, P]) SetRanking(r Ranking[K, P])
SetRanking installs the global ranking algorithm Search boosts scores with (typically an algorithms.Ranking such as PageRank).
func (*InMemoryGraph[K, P]) SetScorer ¶
func (g *InMemoryGraph[K, P]) SetScorer(s scoring.Scorer[K, P])
SetScorer installs the scorer Search folds candidate contributions with. nil is ignored rather than stored: unlike its peers, whose nil means "use the fallback behaviour", a graph without a scorer cannot rank at all.
func (*InMemoryGraph[K, P]) SetTraversal ¶
func (g *InMemoryGraph[K, P]) SetTraversal(t Traversal[K, P])
SetTraversal installs the traversal algorithm Search expands seeds with (typically an algorithms.Traversal such as BFS).
func (*InMemoryGraph[K, P]) Size ¶
func (g *InMemoryGraph[K, P]) Size() int
Size returns the number of relationships (edges) in the graph.
func (*InMemoryGraph[K, P]) Stats ¶
func (g *InMemoryGraph[K, P]) Stats() GraphStats
type IsAbout ¶
type IsAbout[K comparable] struct { Fact *Fact[K] Topic *Topic[K] NodeAttributes Hasher hash.Hasher[K, string] }
Fact is about Topic relationship
func (IsAbout[K]) GetAttributes ¶
func (a IsAbout[K]) GetAttributes() *NodeAttributes
func (IsAbout[K]) GetTimestamp ¶
type Mentions ¶
type Mentions[K comparable] struct { Fact *Fact[K] NamedEntity *NamedEntity[K] NodeAttributes Hasher hash.Hasher[K, string] }
Fact mentions NamedEntity relationship
func (Mentions[K]) GetAttributes ¶
func (m Mentions[K]) GetAttributes() *NodeAttributes
func (Mentions[K]) GetTimestamp ¶
type NamedEntity ¶
type NamedEntity[K comparable] struct { NodeAttributes Hasher hash.Hasher[K, string] `json:"-"` }
func (*NamedEntity[K]) GetAttributes ¶
func (n *NamedEntity[K]) GetAttributes() *NodeAttributes
func (NamedEntity[K]) GetTimestamp ¶
func (n NamedEntity[K]) GetTimestamp() time.Time
func (NamedEntity[K]) GetValue ¶
func (n NamedEntity[K]) GetValue() string
func (NamedEntity[K]) Hash ¶
func (n NamedEntity[K]) Hash(h hash.Hasher[K, string]) K
Hash keys the entity by its name in the entity namespace, so a fact whose whole text is that name is a different node (see Node).
func (NamedEntity[K]) Key ¶
func (n NamedEntity[K]) Key() K
type Node ¶
type Node[K comparable] interface { hash.Hashable[K, string] Key() K GetValue() string GetTimestamp() time.Time GetAttributes() *NodeAttributes }
Node is anything the graph stores under a key: the entities (facts, topics, named entities) and the relationships between them.
A node's key is the hash of its own type tag followed by its material — "fact:", "topic:", "entity:", "mentions:", "isabout:" — so identity is (type, value), never value alone: a fact and a topic that read the same are two different nodes. Without the tag, `remember 'billing' topic:billing` hashes both to one key, and then the topic node is never stored (Set finds the fact already there) while its IsAbout edge points from the fact back to itself. A new participant takes a tag that no other one prefixes; the five above differ in their first byte.
type NodeAttributes ¶
type PageRank ¶
type PageRank[K comparable, P float32 | float64] struct { // contains filtered or unexported fields }
PageRank scores every vertex by the stationary distribution of a random walk that follows an outgoing edge with probability damping and teleports to a uniformly random vertex with probability 1-damping. It iterates until the scores change by less than tol or maxIter iterations are reached. It implements Ranking.
func NewPageRank ¶
func NewPageRank[K comparable, P float32 | float64](damping P, maxIter int, tol P) *PageRank[K, P]
NewPageRank returns a PageRank ranking with the given damping factor, iteration cap and convergence tolerance.
type Ranking ¶
type Ranking[K comparable, P float32 | float64] interface { Algorithm[K, P] // contains filtered or unexported methods }
Ranking assigns a score to every vertex from the graph's global structure, rather than from a single source (PageRank and other centralities).
type RankingResult ¶
type RankingResult[K comparable, P float32 | float64] struct { AlgorithmResult // Scores maps each vertex to its computed score. Scores map[K]P }
RankingResult captures the outcome of a whole-graph ranking.
type Relationship ¶
type Relationship[K comparable] interface { Node[K] Source() *Entity[K] Target() *Entity[K] }
type Topic ¶
type Topic[K comparable] struct { ID K NodeAttributes Hasher hash.Hasher[K, string] `json:"-"` }
func (*Topic[K]) GetAttributes ¶
func (t *Topic[K]) GetAttributes() *NodeAttributes
func (Topic[K]) GetTimestamp ¶
type Traversal ¶
type Traversal[K comparable, P float32 | float64] interface { Algorithm[K, P] // Sets the traversal source SetSource(source K) // Clone returns a fresh traversal with the same configuration but no // per-run state, so each walk can set its own source and run on its own // instance instead of mutating a shared one. Clone() Traversal[K, P] // contains filtered or unexported methods }
Traversal explores a graph starting from a source vertex, visiting reachable vertices in an order defined by the concrete algorithm (breadth-first for BFS, depth-first for DFS). K is the vertex key type and P the graph's score precision.
type TraversalResult ¶
type TraversalResult[K comparable] struct { AlgorithmResult // Order lists the vertices in the order they were first visited. Order []K // Parent maps each visited vertex to the vertex it was discovered from, // forming the traversal tree. The source maps to its own key. Parent map[K]K // Parents maps each visited vertex to every vertex it was reached through // at the previous depth. Parent keeps the canonical (first-in-Order) entry // for tree-shaped consumers; Parents carries the full incidence, because a // member reached through two of the source's anchors is two observations, // not one. Tree-shaped traversals may leave it nil. Parents map[K][]K // Depth maps each visited vertex to its hop distance from the source. Depth map[K]int }
TraversalResult captures the outcome of a source-based traversal.