graph

package
v2.111.1 Latest Latest
Warning

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

Go to latest
Published: Sep 15, 2026 License: MIT Imports: 30 Imported by: 1

Documentation

Index

Constants

View Source
const (
	// RDFTermIRI represents an IRI/resource term.
	RDFTermIRI = "iri"
	// RDFTermBlankNode represents a blank node term.
	RDFTermBlankNode = "blank_node"
	// RDFTermLiteral represents a literal term.
	RDFTermLiteral = "literal"
)
View Source
const (
	DefaultRuleMaxIterations = 16
	DefaultRuleMaxDerived    = 50000
)

Default caps for forward chaining.

A rule set is a program, and a program written in a tool call can loop. The caps are what stops one: not a timeout, which would leave whatever it had managed to write behind, but a hard bound after which ApplyRules writes nothing and says why. Sixteen rounds is far more than transitive closure over a real graph needs — closure over n nodes converges in ceil(log2 n) rounds of this naive chaining — and 50k edges is more than any single call should be adding to a brain without somebody having decided to.

View Source
const (
	SHACLNamespace = "http://www.w3.org/ns/shacl#"
	RDFNamespace   = "http://www.w3.org/1999/02/22-rdf-syntax-ns#"
	XSDNamespace   = "http://www.w3.org/2001/XMLSchema#"

	SHACLNodeShape     = SHACLNamespace + "NodeShape"
	SHACLPropertyShape = SHACLNamespace + "PropertyShape"
	SHACLProperty      = SHACLNamespace + "property"
	SHACLPath          = SHACLNamespace + "path"
	SHACLTargetClass   = SHACLNamespace + "targetClass"
	SHACLTargetNode    = SHACLNamespace + "targetNode"
	SHACLDatatype      = SHACLNamespace + "datatype"
	SHACLMinCount      = SHACLNamespace + "minCount"
	SHACLMaxCount      = SHACLNamespace + "maxCount"
	SHACLMinInclusive  = SHACLNamespace + "minInclusive"
	SHACLMaxInclusive  = SHACLNamespace + "maxInclusive"
	SHACLPattern       = SHACLNamespace + "pattern"
	SHACLIn            = SHACLNamespace + "in"
	SHACLNodeKind      = SHACLNamespace + "nodeKind"
	SHACLClass         = SHACLNamespace + "class"
	SHACLSeverity      = SHACLNamespace + "severity"
	SHACLMessage       = SHACLNamespace + "message"

	SHACLSeverityInfo      = SHACLNamespace + "Info"
	SHACLSeverityWarning   = SHACLNamespace + "Warning"
	SHACLSeverityViolation = SHACLNamespace + "Violation"

	SHACLIRI                = SHACLNamespace + "IRI"
	SHACLBlankNode          = SHACLNamespace + "BlankNode"
	SHACLLiteral            = SHACLNamespace + "Literal"
	SHACLBlankNodeOrIRI     = SHACLNamespace + "BlankNodeOrIRI"
	SHACLBlankNodeOrLiteral = SHACLNamespace + "BlankNodeOrLiteral"
	SHACLIRIOrLiteral       = SHACLNamespace + "IRIOrLiteral"

	RDFType = RDFNamespace + "type"
)

SHACL IRIs

View Source
const (
	// SPARQLQuerySelect executes a tabular SELECT query.
	SPARQLQuerySelect = "select"
	// SPARQLQueryAsk executes a boolean ASK query.
	SPARQLQueryAsk = "ask"
	// SPARQLQueryConstruct executes a graph-producing CONSTRUCT query.
	SPARQLQueryConstruct = "construct"
	// SPARQLQueryDescribe executes a graph-producing DESCRIBE query.
	SPARQLQueryDescribe = "describe"
	// SPARQLQueryInsertData executes an INSERT DATA update.
	SPARQLQueryInsertData = "insert_data"
	// SPARQLQueryDeleteData executes a DELETE DATA update.
	SPARQLQueryDeleteData = "delete_data"
	// SPARQLQueryDeleteWhere executes a DELETE WHERE update.
	SPARQLQueryDeleteWhere = "delete_where"
	// SPARQLQueryModify executes INSERT ... WHERE / DELETE ... INSERT ... WHERE style updates.
	SPARQLQueryModify = "modify"
)
View Source
const DefaultRuleExplainDepth = 4

DefaultRuleExplainDepth is how far ExplainEdge follows the support chain when the caller does not say. Derived edges can support other derived edges, so an explanation is a tree, and an unbounded one over a transitive closure is a wall of text nobody reads.

View Source
const RuleProvenance = "rule"

RuleProvenance is the provenance value written onto every rule-derived edge. The original two-hop inference wrote it, and it is what tells a reader that a rule and not an extractor put the edge there.

Variables

View Source
var (
	BookkeepingNodeTypes = []string{"Decision", "chunk", "document"}
	BookkeepingEdgeTypes = []string{"has_chunk", "mentions", "based_on"}
)

BookkeepingNodeTypes and BookkeepingEdgeTypes are the store's own records — the rows that are not claims about the world.

A decision is a graph node so decision_chain can walk it; a chunk and its document are nodes so an entity can cite one; has_chunk, mentions and based_on are the edges that hold all three in place. None of them carries a grade and none of them ever will, because there is nothing to grade: they are how the store remembers rather than what it knows.

Listed rather than derived from "has no grade", which is the test that put them in the untagged column in the first place. Two rows that carry no grade are different findings — one is a fact nobody stamped and the other is a filing cabinet — and a rule that cannot tell them apart reports the cabinet as the problem.

View Source
var ErrRuleCapExceeded = errors.New("rule derivation cap exceeded")

ErrRuleCapExceeded reports that forward chaining stopped at a cap instead of at a fixpoint. Nothing is written when it is returned: a half-computed closure in the graph is worse than none, because nothing downstream can tell which half it got.

Functions

func AsOf added in v2.99.0

func AsOf(ctx context.Context, at time.Time) context.Context

AsOf is WithReadOptions for the one field it has.

nodes, err := store.ListNodes(graph.AsOf(ctx, before), nil)

func DerivedEdgeID added in v2.98.0

func DerivedEdgeID(ruleID, fromNodeID, toNodeID, edgeType string) string

DerivedEdgeID is the identity of an edge derived by a rule. It deliberately excludes the premises: one rule concluding the same relation between the same two nodes is one edge, however many chains reach it, so a re-run upserts rather than accumulating near-duplicates.

func IsRuleVariable added in v2.98.0

func IsRuleVariable(term string) bool

IsRuleVariable reports whether a term is a variable rather than a literal.

func NotInTypes added in v2.110.0

func NotInTypes(column string, types []string) string

NotInTypes renders "this column is none of these types", for the queries that have to leave the store's own filing out.

Exported because two packages ask the same question and the answer has to be the same one: the tally counts the knowledge in a brain and the live graph draws it, and a picture that included a Decision node while the panel beside it did not count one would be two views of the same store that disagree.

Values are spelled into the SQL rather than bound. They are this package's own constants — nothing here comes from a caller — and threading a variable number of arguments through queries that already bind their own would put the ordering of those arguments at risk for no gain.

func WithReadOptions added in v2.99.0

func WithReadOptions(ctx context.Context, opts ReadOptions) context.Context

WithReadOptions returns a context whose graph reads answer at opts.AsOf.

Types

type AllenRelation added in v2.99.0

type AllenRelation string

AllenRelation names how one interval sits against another.

const (
	AllenBefore       AllenRelation = "before"        // a ends strictly before b starts
	AllenAfter        AllenRelation = "after"         // inverse of before
	AllenMeets        AllenRelation = "meets"         // a ends exactly where b starts
	AllenMetBy        AllenRelation = "met_by"        // inverse of meets
	AllenOverlaps     AllenRelation = "overlaps"      // a starts first, they share a stretch, a ends first
	AllenOverlappedBy AllenRelation = "overlapped_by" // inverse of overlaps
	AllenStarts       AllenRelation = "starts"        // same start, a ends first
	AllenStartedBy    AllenRelation = "started_by"    // inverse of starts
	AllenDuring       AllenRelation = "during"        // a sits strictly inside b
	AllenContains     AllenRelation = "contains"      // inverse of during
	AllenFinishes     AllenRelation = "finishes"      // same end, a starts later
	AllenFinishedBy   AllenRelation = "finished_by"   // inverse of finishes
	AllenEquals       AllenRelation = "equals"        // same start and same end

	// AllenUndefined is returned for an interval that spans nothing. Not an
	// error return: the caller of a diff wants the pairs it can name and the
	// ones it cannot, not a failed query because one row was degenerate.
	AllenUndefined AllenRelation = ""
)

The thirteen, in inverse pairs plus equals. The names are Allen's; the direction is always "a is <relation> b".

func Relate added in v2.99.0

func Relate(a, b Interval) AllenRelation

Relate names how a sits against b. Exactly one relation holds for any two intervals that span anything, which is the property the whole algebra rests on and the reason the switch below has no default case that means "several".

func (AllenRelation) Inverse added in v2.99.0

func (r AllenRelation) Inverse() AllenRelation

Inverse returns the relation that holds in the other direction, so a caller holding `a overlaps b` can state `b overlapped_by a` without recomputing.

type Atom added in v2.98.0

type Atom struct {
	Predicate string `json:"predicate"`
	Subject   string `json:"subject"`
	Object    string `json:"object"`
}

Atom is one edge pattern: predicate(subject, object).

A term starting with '?' is a variable. Anything else is a literal, resolved against the stored nodes — see ResolveRuleTerm for exactly how, because "matches a node" is the one part of this that a caller has to be told rather than able to guess.

func (Atom) String added in v2.98.0

func (a Atom) String() string

String renders an atom as predicate(subject, object).

type BatchEdgeOperation

type BatchEdgeOperation struct {
	Edges []*GraphEdge
}

BatchEdgeOperation represents a batch operation for edges

type BatchGraphOperation

type BatchGraphOperation struct {
	NodeUpserts []*GraphNode
	NodeDeletes []string
	EdgeUpserts []*GraphEdge
	EdgeDeletes []string
}

BatchGraphOperation allows multiple graph operations in a single transaction

type BatchNodeOperation

type BatchNodeOperation struct {
	Nodes []*GraphNode
}

BatchNodeOperation represents a batch operation for nodes

type BatchResult

type BatchResult struct {
	SuccessCount int
	FailedCount  int
	Errors       []error
}

BatchResult contains the results of a batch operation.

A batch is partial by design: one rejected row does not roll the others back, so the batch functions report per-row failures here and reserve their error return for failures of the batch itself (transaction, prepare, commit). That makes it easy to lose writes without noticing — a caller that only checks err sees success for a batch in which every row was rejected. Call Err to fold the per-row failures back into a normal error before reporting success upwards.

func (*BatchResult) Err added in v2.69.0

func (r *BatchResult) Err() error

Err returns the per-row failures of a batch joined into a single error, or nil when every row the caller supplied was written.

Only rows that actually failed are reported: the delete batches count ids that matched nothing as failed without recording an error, because "not there" is not a failure to delete. The upsert batches record an error for every failure they count.

type Community

type Community struct {
	ID    int      `json:"id"`
	Nodes []string `json:"nodes"`
	Score float64  `json:"score"` // Modularity score
}

Community represents a detected community of nodes

type Connectivity added in v2.86.0

type Connectivity struct {
	Nodes   int `json:"nodes"`
	Orphans int `json:"orphans"`
}

Connectivity is how much of the graph an edge can reach.

type DiffKind added in v2.99.0

type DiffKind string

DiffKind is why a row is in the diff.

const (
	// DiffAdded: present at `to`, absent at `from`.
	DiffAdded DiffKind = "added"
	// DiffRetracted: present at `from`, absent at `to`. Retracted rather than
	// deleted, because that is what it is now — the row is still readable at
	// any instant before it went.
	DiffRetracted DiffKind = "retracted"
	// DiffChanged: present at both, saying something different.
	DiffChanged DiffKind = "changed"
)

type DiffOptions added in v2.99.0

type DiffOptions struct {
	// Limit caps each of the two lists. Zero means 100; a diff is a report, and
	// an unbounded one is a report nobody reads and a response nobody can pass
	// to a model.
	Limit int
	// Cursor continues a previous diff. It is the last id that page emitted;
	// nodes and edges are paged together by the same id, which is safe because
	// both streams are ordered by it.
	Cursor string
	// NodeTypes and EdgeTypes narrow the diff to a kind of thing.
	NodeTypes []string
	EdgeTypes []string
	// MaxIntervalPairs caps the Allen relations reported. Zero means 50.
	MaxIntervalPairs int
}

DiffOptions bounds a diff.

type EdgeChange added in v2.99.0

type EdgeChange struct {
	ID     string       `json:"id"`
	Kind   DiffKind     `json:"kind"`
	Before *EdgeVersion `json:"before,omitempty"`
	After  *EdgeVersion `json:"after,omitempty"`
}

type EdgeEndpoint added in v2.85.0

type EdgeEndpoint struct {
	ID       string `json:"id"`
	NodeType string `json:"node_type"`
	Content  string `json:"content"`
}

EdgeEndpoint identifies one end of an edge, with enough about the node to name it in a report without a second round trip.

type EdgeEndpointPair added in v2.85.0

type EdgeEndpointPair struct {
	EdgeType string       `json:"edge_type"`
	From     EdgeEndpoint `json:"from"`
	To       EdgeEndpoint `json:"to"`
	Count    int          `json:"count"`
}

EdgeEndpointPair is one (edge type, from node, to node) combination and how many edges join exactly those two nodes with that type.

type EdgePrediction

type EdgePrediction struct {
	FromNodeID string  `json:"from_node_id"`
	ToNodeID   string  `json:"to_node_id"`
	Score      float64 `json:"score"`
	Method     string  `json:"method"`
}

EdgePrediction represents a predicted edge with confidence score

type EdgeShape added in v2.85.0

type EdgeShape struct {
	EdgeType string `json:"edge_type"`
	FromType string `json:"from_type"`
	ToType   string `json:"to_type"`
	Count    int    `json:"count"`
}

EdgeShape is one (edge type, from-node type, to-node type) combination present in the graph, and how many edges have it.

type EdgeVersion added in v2.99.0

type EdgeVersion struct {
	ID         string    `json:"id"`
	From       string    `json:"from"`
	To         string    `json:"to"`
	EdgeType   string    `json:"edge_type,omitempty"`
	Weight     float64   `json:"weight,omitempty"`
	Properties string    `json:"properties,omitempty"`
	ValidFrom  time.Time `json:"valid_from,omitzero"`
	ValidTo    time.Time `json:"valid_to,omitzero"`
}

EdgeVersion is an edge as it stood at one instant.

func (EdgeVersion) Interval added in v2.99.0

func (v EdgeVersion) Interval() Interval

Interval is this version's validity, for Relate.

type ExportFormat

type ExportFormat string

ExportFormat represents supported export formats

const (
	FormatGraphML ExportFormat = "graphml"
	FormatGEXF    ExportFormat = "gexf"
	FormatJSON    ExportFormat = "json"
)

func DetectFormat

func DetectFormat(reader io.Reader) (ExportFormat, error)

DetectFormat attempts to detect the format of the input

type GEXFAttValue

type GEXFAttValue struct {
	For   string `xml:"for,attr"`
	Value string `xml:"value,attr"`
}

GEXFAttValue represents a GEXF attribute value

type GEXFAttr

type GEXFAttr struct {
	ID    string `xml:"id,attr"`
	Title string `xml:"title,attr"`
	Type  string `xml:"type,attr"`
}

GEXFAttr represents a GEXF attribute

type GEXFAttrs

type GEXFAttrs struct {
	Class string     `xml:"class,attr"`
	Attrs []GEXFAttr `xml:"attribute"`
}

GEXFAttrs represents GEXF attributes

type GEXFDocument

type GEXFDocument struct {
	XMLName xml.Name  `xml:"gexf"`
	XMLNS   string    `xml:"xmlns,attr"`
	Version string    `xml:"version,attr"`
	Meta    GEXFMeta  `xml:"meta"`
	Graph   GEXFGraph `xml:"graph"`
}

GEXFDocument represents a GEXF document

type GEXFEdge

type GEXFEdge struct {
	ID     string  `xml:"id,attr"`
	Source string  `xml:"source,attr"`
	Target string  `xml:"target,attr"`
	Weight float64 `xml:"weight,attr,omitempty"`
	Type   string  `xml:"type,attr,omitempty"`
}

GEXFEdge represents a GEXF edge

type GEXFEdges

type GEXFEdges struct {
	Edges []GEXFEdge `xml:"edge"`
}

GEXFEdges represents GEXF edges container

type GEXFGraph

type GEXFGraph struct {
	Mode            string    `xml:"mode,attr"`
	DefaultEdgeType string    `xml:"defaultedgetype,attr"`
	Attributes      GEXFAttrs `xml:"attributes"`
	Nodes           GEXFNodes `xml:"nodes"`
	Edges           GEXFEdges `xml:"edges"`
}

GEXFGraph represents a GEXF graph

type GEXFMeta

type GEXFMeta struct {
	Creator     string `xml:"creator"`
	Description string `xml:"description"`
}

GEXFMeta represents GEXF metadata

type GEXFNode

type GEXFNode struct {
	ID        string         `xml:"id,attr"`
	Label     string         `xml:"label,attr"`
	AttValues []GEXFAttValue `xml:"attvalues>attvalue"`
}

GEXFNode represents a GEXF node

type GEXFNodes

type GEXFNodes struct {
	Nodes []GEXFNode `xml:"node"`
}

GEXFNodes represents GEXF nodes container

type GraphDiffResult added in v2.99.0

type GraphDiffResult struct {
	From  time.Time    `json:"from"`
	To    time.Time    `json:"to"`
	Nodes []NodeChange `json:"nodes"`
	Edges []EdgeChange `json:"edges"`
	// NextCursor is non-empty when the limit cut the walk short. Pass it back
	// as DiffOptions.Cursor.
	NextCursor string `json:"next_cursor,omitempty"`
	Truncated  bool   `json:"truncated,omitempty"`
	// IntervalRelations names how the changed edges about one subject sit
	// against each other in time — which is where "the runbook's claim ended
	// when the incident's began" comes from, instead of two rows of timestamps
	// a reader has to compare by eye.
	IntervalRelations []IntervalRelation `json:"interval_relations,omitempty"`
}

GraphDiffResult is what changed, and how the changed facts sit in time.

type GraphEdge

type GraphEdge struct {
	ID         string                 `json:"id"`
	FromNodeID string                 `json:"from_node_id"`
	ToNodeID   string                 `json:"to_node_id"`
	EdgeType   string                 `json:"edge_type,omitempty"`
	Weight     float64                `json:"weight"`
	Properties map[string]interface{} `json:"properties,omitempty"`
	Vector     []float32              `json:"vector,omitempty"` // Optional edge embedding
	CreatedAt  time.Time              `json:"created_at"`

	// The bitemporal columns, with the same rules as GraphNode's: ValidFrom is
	// an input, the other three are outputs. An edge is where these earn their
	// keep — "the runbook recommended sds-meta" is a claim with a beginning and
	// an end, and Relate names how two such claims sit against each other.
	ValidFrom   time.Time `json:"valid_from,omitzero"`
	ValidTo     time.Time `json:"valid_to,omitzero"`
	RecordedAt  time.Time `json:"recorded_at,omitzero"`
	RetractedAt time.Time `json:"retracted_at,omitzero"`
}

GraphEdge represents a directed edge between two nodes

type GraphFilter

type GraphFilter struct {
	NodeTypes []string `json:"node_types,omitempty"`
	EdgeTypes []string `json:"edge_types,omitempty"`
	MaxDepth  int      `json:"max_depth,omitempty"`
	// Properties scopes a query to nodes whose properties JSON carries every
	// one of these top-level string fields with these values.
	//
	// It exists because a store this one shares with everything else on the
	// machine had no way to ask for one importer's rows. node_type was the
	// only filter, and a type name is not a batch: an importer that wrote a
	// thousand Person nodes on Tuesday and a thousand more on Friday could
	// not ask for either set, only for all two thousand. Every writer that
	// cared already stamped a batch onto properties — alchemy's connector
	// writes "run" — and nothing could read it back.
	//
	// The fields are ANDed, and each is compared as text: properties is a
	// JSON object serialized by json.Marshal, and the guarded read in
	// pkg/sqldialect is what keeps a row with no properties at all from
	// failing the whole query rather than simply not matching.
	Properties map[string]string `json:"properties,omitempty"`
	// Contains narrows to nodes whose property holds this text somewhere in
	// it, compared case-insensitively and ANDed with everything else.
	//
	// Separate from Properties because the two are different questions and
	// only one of them can use an index: Properties is how a caller names a
	// batch it already knows, Contains is how a search enters the graph at
	// all. Folding them into one map would make every batch scope a LIKE.
	//
	// The value is matched literally — % and _ in it mean those characters,
	// not wildcards — because the text comes from whoever typed the query and
	// a name with an underscore in it is ordinary.
	Contains map[string]string `json:"contains,omitempty"`
	// Limit caps the rows a query returns. Zero means no cap, which is what
	// every caller before this field got and still gets.
	//
	// A cap alone would be worse than none: a caller shown 100 of 4000 rows
	// with nothing saying so reports 100. CountNodes answers the other half,
	// and the two are meant to be used together.
	Limit int `json:"limit,omitempty"`
}

GraphFilter defines filtering options for graph queries

type GraphMLData

type GraphMLData struct {
	Key   string `xml:"key,attr"`
	Value string `xml:",chardata"`
}

GraphMLData represents GraphML data

type GraphMLDocument

type GraphMLDocument struct {
	XMLName xml.Name     `xml:"graphml"`
	XMLNS   string       `xml:"xmlns,attr"`
	Keys    []GraphMLKey `xml:"key"`
	Graph   GraphMLGraph `xml:"graph"`
}

GraphMLDocument represents a GraphML document

type GraphMLEdge

type GraphMLEdge struct {
	ID     string        `xml:"id,attr"`
	Source string        `xml:"source,attr"`
	Target string        `xml:"target,attr"`
	Data   []GraphMLData `xml:"data"`
}

GraphMLEdge represents a GraphML edge

type GraphMLGraph

type GraphMLGraph struct {
	ID          string        `xml:"id,attr"`
	EdgeDefault string        `xml:"edgedefault,attr"`
	Nodes       []GraphMLNode `xml:"node"`
	Edges       []GraphMLEdge `xml:"edge"`
}

GraphMLGraph represents a GraphML graph

type GraphMLKey

type GraphMLKey struct {
	ID       string `xml:"id,attr"`
	For      string `xml:"for,attr"`
	AttrName string `xml:"attr.name,attr"`
	AttrType string `xml:"attr.type,attr"`
}

GraphMLKey represents a GraphML key definition

type GraphMLNode

type GraphMLNode struct {
	ID   string        `xml:"id,attr"`
	Data []GraphMLData `xml:"data"`
}

GraphMLNode represents a GraphML node

type GraphNode

type GraphNode struct {
	ID         string                 `json:"id"`
	Vector     []float32              `json:"vector"`
	Content    string                 `json:"content,omitempty"`
	NodeType   string                 `json:"node_type,omitempty"`
	Properties map[string]interface{} `json:"properties,omitempty"`
	CreatedAt  time.Time              `json:"created_at"`
	UpdatedAt  time.Time              `json:"updated_at"`

	// The bitemporal columns. See pkg/graph/temporal.go for what the two axes
	// mean and why NULL is unbounded on all four.
	//
	// ValidFrom is the only one a caller may set on a write: it says when the
	// fact became true in the world, and defaults to the moment of the write.
	// The other three are set by the store and ignored on write, the same way
	// CreatedAt and UpdatedAt already were — a live row always has an open
	// ValidTo and no RetractedAt, because a row that has ended or been
	// retracted lives in graph_node_history instead.
	ValidFrom   time.Time `json:"valid_from,omitzero"`
	ValidTo     time.Time `json:"valid_to,omitzero"`
	RecordedAt  time.Time `json:"recorded_at,omitzero"`
	RetractedAt time.Time `json:"retracted_at,omitzero"`
}

GraphNode represents a node in the graph with vector embedding

type GraphResult

type GraphResult struct {
	Nodes []*GraphNode `json:"nodes"`
	Edges []*GraphEdge `json:"edges"`
}

GraphResult represents a subgraph or query result

type GraphStatistics

type GraphStatistics struct {
	NodeCount           int     `json:"node_count"`
	EdgeCount           int     `json:"edge_count"`
	AverageDegree       float64 `json:"average_degree"`
	Density             float64 `json:"density"`
	ConnectedComponents int     `json:"connected_components"`
}

GraphStatistics represents overall graph statistics

type GraphStore

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

GraphStore provides graph operations on top of the vector store

func NewGraphStore

func NewGraphStore(s *core.SQLiteStore) *GraphStore

NewGraphStore creates a new graph store from a SQLite store.

func NewGraphStoreOn added in v2.82.0

func NewGraphStoreOn(db *sql.DB, d sqldialect.Dialect, host vectorHost) *GraphStore

NewGraphStoreOn creates a graph store over any database this dialect can speak, for a host that supplies the vector-side settings.

The same tables and the same queries as NewGraphStore — the graph layer has no SQLite-only SQL in it, which is what makes one implementation over two databases possible rather than two implementations to keep in step.

func (*GraphStore) ApplyRules added in v2.98.0

func (g *GraphStore) ApplyRules(ctx context.Context, rules []Rule, opts RuleOptions) (*RuleApplyResult, error)

ApplyRules forward-chains rules over the stored graph to a fixpoint and materializes what it derives.

Every derived edge carries inferred=true, provenance=rule, the rule's id and text, the exact premise edge ids, and a confidence that is the minimum of the premise confidences times the rule's own — the same provenance the two-hop inference wrote, which is what lets ExplainEdge explain either.

A run that hits a cap writes nothing and returns ErrRuleCapExceeded along with a result whose CapReason says which cap. Both are returned: the error is what a caller cannot ignore, the result is what tells them why.

func (*GraphStore) ArchiveEdgesTx added in v2.99.0

func (g *GraphStore) ArchiveEdgesTx(ctx context.Context, tx *sql.Tx, edgeIDs []string, at time.Time) error

ArchiveEdgesTx is ArchiveNodesTx for edges the caller deletes itself.

func (*GraphStore) ArchiveNodesTx added in v2.99.0

func (g *GraphStore) ArchiveNodesTx(ctx context.Context, tx *sql.Tx, nodeIDs []string, at time.Time) error

ArchiveNodesTx moves nodes and every edge touching them into history with retracted_at set, without deleting anything.

Exported and transaction-taking for the one caller shape that cannot use RetractNodes: pkg/cortexdb deletes a document's whole graph inside a transaction of its own, and splitting the archive out of that transaction would make a crash between them lose the history of rows that did get deleted. Call it immediately before the DELETE, in the same transaction.

Edges go with the node because a retracted node's edges are retracted too — graph_edges has ON DELETE CASCADE on both ends, so the DELETE that follows removes them whether or not the caller listed them, and an edge deleted with no history row is a fact that vanished.

func (*GraphStore) BookkeepingCount added in v2.110.0

func (g *GraphStore) BookkeepingCount(ctx context.Context) (PropertyCount, error)

BookkeepingCount counts them.

func (*GraphStore) CommunityDetection

func (g *GraphStore) CommunityDetection(ctx context.Context) ([]Community, error)

CommunityDetection performs community detection using the Louvain method Optimized to reduce DB queries.

func (*GraphStore) CompactIRI added in v2.16.0

func (g *GraphStore) CompactIRI(ctx context.Context, value string) (string, error)

CompactIRI compacts an IRI using the longest matching namespace prefix when available.

func (*GraphStore) Connected

func (g *GraphStore) Connected(ctx context.Context, nodeID1, nodeID2 string, maxDepth int) (bool, error)

Connected checks if two nodes are connected within a given depth

func (*GraphStore) Connectivity added in v2.86.0

func (g *GraphStore) Connectivity(ctx context.Context) (Connectivity, error)

Connectivity counts the nodes no edge touches, alongside the total.

A large share of orphans means writes landed and their edges did not — the state a store reaches when an ingest's edges are rejected, or when entities accumulate across re-ingests with nothing joining them. Retrieval still finds these nodes and expansion never leaves them, so the graph half of GraphRAG quietly stops contributing while every count still grows.

Both numbers come from one statement so they describe one graph. Asked separately, a write between them yields a share that was never true — and this is a health check, whose whole output is that ratio.

Distinct from GraphStatistics.ConnectedComponents, which asks how the reachable part is divided; this asks how much of the graph is reachable at all.

func (*GraphStore) CountNodes added in v2.89.0

func (g *GraphStore) CountNodes(ctx context.Context, filter *GraphFilter) (int, error)

CountNodes reports how many nodes match a filter, ignoring its Limit.

It is the other half of GraphFilter.Limit and it is not optional decoration. A caller that asks for 100 nodes and is handed 100 has learned nothing about whether there were 100 or 4000, and the honest failure — "showing 100 of 4000" — is unavailable without this. Callers that report a total to a person or to a model should ask for both.

Limit is ignored on purpose: a count that stopped at the cap would always equal the cap, which is the exact non-answer this method exists to replace.

func (*GraphStore) DeleteEdge

func (g *GraphStore) DeleteEdge(ctx context.Context, edgeID string) error

DeleteEdge removes an edge from the current graph.

A retraction, like DeleteNode: the row moves to graph_edge_history with retracted_at set, so "what did this fact say before we withdrew it" has an answer and FactProvenanceFor can still resolve it as of an earlier instant. Current reads see exactly what they saw before.

func (*GraphStore) DeleteEdgesBatch

func (g *GraphStore) DeleteEdgesBatch(ctx context.Context, edgeIDs []string) (*BatchResult, error)

DeleteEdgesBatch deletes multiple edges in a single transaction

func (*GraphStore) DeleteNamespace added in v2.16.0

func (g *GraphStore) DeleteNamespace(ctx context.Context, prefix string) error

DeleteNamespace removes one user-defined namespace mapping.

func (*GraphStore) DeleteNode

func (g *GraphStore) DeleteNode(ctx context.Context, nodeID string) error

DeleteNode removes a node and all its edges from the current graph.

It is a retraction now, not a hard delete: the node and its edges move to graph_node_history / graph_edge_history with retracted_at set, so a read as of any instant before this one still sees them. Every current read is unaffected — the live tables no longer hold the row — which is why the name, the signature and the "node not found" error are all unchanged.

RetractNodeAt is the same operation with the instant stated, for a fact discovered today to have stopped being believed last Tuesday. Purge is the only thing that removes any of it for good.

func (*GraphStore) DeleteNodesBatch

func (g *GraphStore) DeleteNodesBatch(ctx context.Context, nodeIDs []string) (*BatchResult, error)

DeleteNodesBatch deletes multiple nodes in a single transaction

func (*GraphStore) DeleteRule added in v2.98.0

func (g *GraphStore) DeleteRule(ctx context.Context, id string) (bool, error)

DeleteRule removes a stored rule and reports whether there was one.

It does not touch the edges that rule derived. Deleting a rule is a statement about what will be derived next time, not a retraction of what is already in the graph — and the derived edges carry the rule text with them, so they remain explicable after the rule is gone.

func (*GraphStore) DeleteTriple added in v2.16.0

func (g *GraphStore) DeleteTriple(ctx context.Context, triple RDFTriple) error

DeleteTriple removes one RDF triple/quad by its normalized content.

func (*GraphStore) DeleteTriples added in v2.16.0

func (g *GraphStore) DeleteTriples(ctx context.Context, pattern TriplePattern) (int, error)

DeleteTriples removes all triples matched by the given pattern.

func (*GraphStore) EdgeEndpointPairs added in v2.85.0

func (g *GraphStore) EdgeEndpointPairs(ctx context.Context, edgeTypes ...string) ([]EdgeEndpointPair, error)

EdgeEndpointPairs reports which nodes the edges of each type actually run between.

EdgeShapes groups by type, which answers "how is this relation wrong" and not "what is wrong". On a live base eighteen non-conforming edges came from thirteen nodes, two of which carried six between them — six edges, two places to look. Getting from one answer to the other needs the nodes, and getting the nodes needs their identity, which is why this returns content and type alongside the id rather than a list of ids to look up afterwards.

Ordering, filtering, and the treatment of missing endpoints are the same as EdgeShapes. This scans one row per distinct (type, from, to) triple, so on a large graph pass the edge types the caller actually cares about.

func (*GraphStore) EdgeShapes added in v2.85.0

func (g *GraphStore) EdgeShapes(ctx context.Context, edgeTypes ...string) ([]EdgeShape, error)

EdgeShapes reports the type shapes the graph's edges actually have.

This is what makes a declared relation checkable. An extracted `backs` asserted from the resource to the pool is stored without complaint, reads as a fact, and is walked as one; nothing in the answer it produces says the arrow points the wrong way. Comparing these shapes against the declared ends is the only thing that can disagree with it.

Passing edge types narrows the scan to those; passing none reports every shape in the graph. They are matched exactly, as stored — the same rule the traversal filters use, since the type in the graph is whatever the extracting model emitted and this package nowhere decides what a near-miss means.

Edges whose endpoints are missing are not reported: both endpoints are declared foreign keys, so on a store this package opened they cannot exist.

func (*GraphStore) EdgeSource added in v2.99.0

func (g *GraphStore) EdgeSource(ctx context.Context) (string, []any)

func (*GraphStore) EdgeTypeCounts added in v2.85.0

func (g *GraphStore) EdgeTypeCounts(ctx context.Context) (map[string]int, error)

EdgeTypeCounts returns how many edges carry each edge_type, with untyped edges under the empty string for the same reason as NodeTypeCounts.

func (*GraphStore) EnableHNSWIndex

func (g *GraphStore) EnableHNSWIndex(dimensions int) error

EnableHNSWIndex enables HNSW indexing for the graph store

func (*GraphStore) ExecuteBatch

func (g *GraphStore) ExecuteBatch(ctx context.Context, ops *BatchGraphOperation) (*BatchResult, error)

ExecuteBatch executes multiple graph operations in a single transaction

func (*GraphStore) ExecuteBatchTx added in v2.14.1

func (g *GraphStore) ExecuteBatchTx(ctx context.Context, tx *sql.Tx, ops *BatchGraphOperation) (*BatchResult, error)

ExecuteBatchTx applies a batch of graph operations inside an existing transaction.

func (*GraphStore) ExecuteSPARQL added in v2.16.0

func (g *GraphStore) ExecuteSPARQL(ctx context.Context, query string) (*SPARQLResult, error)

ExecuteSPARQL runs a practical SPARQL SELECT/ASK subset against the embedded RDF layer.

func (*GraphStore) ExpandIRI added in v2.16.0

func (g *GraphStore) ExpandIRI(ctx context.Context, value string) (string, error)

ExpandIRI expands a compact IRI using registered namespaces when possible.

func (*GraphStore) ExplainEdge added in v2.98.0

func (g *GraphStore) ExplainEdge(ctx context.Context, edgeID string) (*RuleEdgeExplanation, error)

ExplainEdge returns the immediate derivation of one edge: whether it was inferred, by which rule, and which edges it stands on.

func (*GraphStore) ExplainEdgeTrace added in v2.98.0

func (g *GraphStore) ExplainEdgeTrace(ctx context.Context, edgeID string, depth int) ([]RuleEdgeTraceEntry, error)

ExplainEdgeTrace follows the premise chain up to depth levels and returns it flattened in preorder. Depth zero means DefaultRuleExplainDepth.

func (*GraphStore) ExplainTriple added in v2.16.0

func (g *GraphStore) ExplainTriple(ctx context.Context, tripleID string) (*RDFSInferenceExplanation, error)

ExplainTriple returns whether a triple is explicit or inferred and its immediate provenance.

func (*GraphStore) ExplainTripleTrace added in v2.16.0

func (g *GraphStore) ExplainTripleTrace(ctx context.Context, tripleID string, depth int) ([]RDFSInferenceTraceEntry, error)

ExplainTripleTrace recursively expands provenance for a triple into a flattened trace list.

func (*GraphStore) ExplainTriplesByPattern added in v2.18.0

func (g *GraphStore) ExplainTriplesByPattern(ctx context.Context, pattern TriplePattern, depth int) ([]RDFSInferenceMatchExplanation, error)

ExplainTriplesByPattern expands explanations for all triples matched by the given pattern.

func (*GraphStore) Export

func (g *GraphStore) Export(ctx context.Context, writer io.Writer, format ExportFormat) error

Export exports the graph in the specified format

func (*GraphStore) ExportGEXF

func (g *GraphStore) ExportGEXF(ctx context.Context, writer io.Writer) error

ExportGEXF exports the graph to GEXF format

func (*GraphStore) ExportGraphML

func (g *GraphStore) ExportGraphML(ctx context.Context, writer io.Writer) error

ExportGraphML exports the graph to GraphML format

func (*GraphStore) ExportJSON

func (g *GraphStore) ExportJSON(ctx context.Context, writer io.Writer) error

ExportJSON exports the graph to JSON format

func (*GraphStore) ExportRDF added in v2.16.0

func (g *GraphStore) ExportRDF(ctx context.Context, writer io.Writer, format RDFFormat) error

ExportRDF writes triples in the requested RDF format.

func (*GraphStore) FindTriples added in v2.16.0

func (g *GraphStore) FindTriples(ctx context.Context, pattern TriplePattern) ([]RDFTriple, error)

FindTriples queries triples by pattern.

func (*GraphStore) GetAllNodes

func (g *GraphStore) GetAllNodes(ctx context.Context, filter *GraphFilter) ([]*GraphNode, error)

GetAllNodes retrieves all nodes with optional filtering

func (*GraphStore) GetEdges

func (g *GraphStore) GetEdges(ctx context.Context, nodeID string, direction string) ([]*GraphEdge, error)

GetEdges retrieves edges for a node.

Ordered by id, which matters more than it looks: this feeds Neighbors, and Neighbors feeds graph-mode retrieval. Without an ORDER BY, SQLite happens to return insertion order while PostgreSQL returns whatever the plan produced — so the same question could retrieve a different set of chunks on the two backends, and a different set on PostgreSQL from one run to the next once the table had been updated.

func (*GraphStore) GetEdgesBatch

func (g *GraphStore) GetEdgesBatch(ctx context.Context, edgeIDs []string) ([]*GraphEdge, error)

GetEdgesBatch retrieves multiple edges by their IDs

func (*GraphStore) GetGraphStatistics

func (g *GraphStore) GetGraphStatistics(ctx context.Context) (*GraphStatistics, error)

GetGraphStatistics computes statistics about the graph

func (*GraphStore) GetNode

func (g *GraphStore) GetNode(ctx context.Context, nodeID string) (*GraphNode, error)

GetNode retrieves a node by ID

func (*GraphStore) GetNodesBatch

func (g *GraphStore) GetNodesBatch(ctx context.Context, nodeIDs []string) ([]*GraphNode, error)

GetNodesBatch retrieves multiple nodes by their IDs.

The result contains only the ids that exist, in no guaranteed order, which is what it always did — chunking does not change that contract.

func (*GraphStore) GetRule added in v2.98.0

func (g *GraphStore) GetRule(ctx context.Context, id string) (*StoredRule, error)

GetRule returns one stored rule, or nil when there is no such rule.

func (*GraphStore) GetTriple added in v2.16.0

func (g *GraphStore) GetTriple(ctx context.Context, id string) (*RDFTriple, error)

GetTriple fetches one RDF triple by ID.

func (*GraphStore) GraphDiff added in v2.99.0

func (g *GraphStore) GraphDiff(ctx context.Context, from, to time.Time, opts DiffOptions) (*GraphDiffResult, error)

GraphDiff reports what the graph gained, lost and changed between two instants.

Both instants are read through the same as-of machinery as any other past read, so a diff and a pair of snapshots can never disagree. `from` after `to` is refused rather than silently swapped: a caller that has them backwards is asking a different question than the one it would get.

func (*GraphStore) GraphVectorSearch

func (g *GraphStore) GraphVectorSearch(ctx context.Context, startNodeID string, vector []float32, opts TraversalOptions) ([]*HybridResult, error)

GraphVectorSearch performs vector search within a graph neighborhood

func (*GraphStore) HNSWHybridSearch

func (g *GraphStore) HNSWHybridSearch(ctx context.Context, query *HybridQuery) ([]*HybridResult, error)

HNSWHybridSearch combines HNSW search with graph proximity

func (*GraphStore) HNSWSearch

func (g *GraphStore) HNSWSearch(ctx context.Context, query []float32, k int, threshold float64) ([]*HybridResult, error)

HNSWSearch performs HNSW-accelerated vector search

func (*GraphStore) HybridSearch

func (g *GraphStore) HybridSearch(ctx context.Context, query *HybridQuery) ([]*HybridResult, error)

HybridSearch performs a combined vector and graph search

func (*GraphStore) Import

func (g *GraphStore) Import(ctx context.Context, reader io.Reader, format ExportFormat) error

Import imports a graph in the specified format

func (*GraphStore) ImportGEXF

func (g *GraphStore) ImportGEXF(ctx context.Context, reader io.Reader) error

ImportGEXF imports a graph from GEXF format

func (*GraphStore) ImportGraphML

func (g *GraphStore) ImportGraphML(ctx context.Context, reader io.Reader) error

ImportGraphML imports a graph from GraphML format

func (*GraphStore) ImportJSON

func (g *GraphStore) ImportJSON(ctx context.Context, reader io.Reader) error

ImportJSON imports a graph from JSON format

func (*GraphStore) ImportRDF added in v2.16.0

func (g *GraphStore) ImportRDF(ctx context.Context, reader io.Reader, format RDFFormat) (int, error)

ImportRDF parses and stores triples from supported RDF serializations.

func (*GraphStore) InferenceSummary added in v2.18.0

func (g *GraphStore) InferenceSummary(ctx context.Context) (*RDFSInferenceSummary, error)

InferenceSummary returns explicit/inferred counts and an inference-rule breakdown.

func (*GraphStore) InitGraphSchema

func (g *GraphStore) InitGraphSchema(ctx context.Context) error

InitGraphSchema creates the graph tables if they don't exist.

It is cheap to call repeatedly: after the first success this store remembers that its schema is ready and returns without touching SQLite, so write paths can guard themselves with it instead of trusting the caller to have done so.

func (*GraphStore) ListNamespaces added in v2.16.0

func (g *GraphStore) ListNamespaces(ctx context.Context) ([]Namespace, error)

ListNamespaces returns built-in and user-defined namespaces.

func (*GraphStore) ListNodes added in v2.90.0

func (g *GraphStore) ListNodes(ctx context.Context, filter *GraphFilter) ([]*GraphNode, error)

ListNodes is GetAllNodes without the vectors.

The projection is the whole of it, and it is not a micro-optimisation. A vector is the largest column on a node — 768 floats is three kilobytes — and GetAllNodes selects it, decodes it, and hands it to a caller that mostly wants to know what things are called. Enumerating the types in a four-hundred-thousand-node import through GetAllNodes moves a gigabyte of embeddings across the driver and decodes every one of them to count names.

It shares nodeWhere with GetAllNodes and CountNodes, so all three are always about the same rows: a list, a count and a filtered read that disagreed about what matched would be worse than any one of them alone.

Nodes come back with a nil Vector. That is the honest shape — a zero-length vector read from a store that holds one would be a lie about the record, and a caller who needs it has GetAllNodes or GetNode.

func (*GraphStore) ListRules added in v2.98.0

func (g *GraphStore) ListRules(ctx context.Context, onlyEnabled bool) ([]StoredRule, error)

ListRules returns the stored rules in id order. Passing onlyEnabled skips the ones somebody has switched off without deleting.

func (*GraphStore) MergeEntities added in v2.26.0

func (g *GraphStore) MergeEntities(ctx context.Context, canonicalID string, aliasIDs []string) error

MergeEntities collapses surface-form duplicate nodes into one canonical node: every edge referencing an alias is repointed to canonicalID, self-loops created by the merge are dropped, and the alias nodes are deleted. Used for entity resolution (e.g. unifying "r0" / "DRBD resource" / "resources" into one entity).

func (*GraphStore) Neighbors

func (g *GraphStore) Neighbors(ctx context.Context, nodeID string, opts TraversalOptions) ([]*GraphNode, error)

Neighbors performs a breadth-first search to find neighboring nodes

func (*GraphStore) NodeLabels added in v2.86.0

func (g *GraphStore) NodeLabels(ctx context.Context, q NodeLabelQuery) ([]NodeLabel, error)

NodeLabels lists nodes with their type and label.

The question behind it is "what is in here, called what" — which spellings a vocabulary actually produced, whether one concept arrived under two names, which labels are long enough to match text against. Callers were reading graph_nodes for it directly, and pairing a schema they are not promised with an id convention they are not promised either.

Ordered by id so two runs over one graph agree, and so a caller paging with Limit sees a stable sequence rather than whichever rows the planner returned first.

func (*GraphStore) NodePropertyKeys added in v2.94.0

func (g *GraphStore) NodePropertyKeys(ctx context.Context, nodeTypes ...string) ([]PropertyKeyUsage, error)

NodePropertyKeys reports which property keys the nodes of each type carry.

Every other property query in this file starts from a key the caller already knows. This one starts from nothing, because that is where anything deriving a shape from stored data has to start: what the records say about themselves, before anybody has written down what they were supposed to say. A caller wanting it had to enumerate JSON keys itself — which is json_each on SQLite and a lateral jsonb_each_text on PostgreSQL, so it was also a caller pinned to one database.

Passing node types narrows the scan to those; passing none reports every type in the graph. They are matched exactly, as stored, and untyped nodes come back under the empty string — the same two rules NodeTypeCounts and EdgeShapes follow, and for the same reasons.

Domain-neutral, like the rest of this section: it knows that properties is a JSON object of string fields, and nothing about what any key means or what anybody intends to do with the answer.

One row per (type, key) pair, so the cost is a scan of graph_nodes and its properties rather than a scan per type. Ordered by type then key so two reads of one graph agree.

func (*GraphStore) NodeSource added in v2.99.0

func (g *GraphStore) NodeSource(ctx context.Context) (string, []any)

NodeSource and EdgeSource name the rows a read should see: a table name to put in a FROM clause, and the arguments it binds ahead of the query's own.

With no as-of on the context they return the bare table name, so the query the caller writes is byte-for-byte the query it was before this file existed — no subquery, no extra predicate, nothing for the planner to reconsider. Only a past read pays for the union of the live table and its history.

The caller appends its own alias and puts these arguments first, in the order the sources appear in the SQL text:

src, args := g.EdgeSource(ctx)
rows, err := db.Query(`SELECT id FROM `+src+` AS e WHERE edge_type = ?`,
    append(args, "mentions")...)

Exported because pkg/cortexdb writes a good deal of its own SQL against graph_nodes and graph_edges, and a read there that ignored the as-of would be a read that silently answered a different question from the one beside it.

func (*GraphStore) NodeTypeCounts added in v2.85.0

func (g *GraphStore) NodeTypeCounts(ctx context.Context) (map[string]int, error)

NodeTypeCounts returns how many nodes carry each node_type.

Untyped nodes are counted under the empty string rather than dropped. A caller that sums these and compares against GetGraphStatistics should get the same number, and "there are 400 nodes nobody typed" is a finding in its own right — silently omitting them turns it into a discrepancy the caller has to explain.

func (*GraphStore) Now added in v2.99.0

func (g *GraphStore) Now() time.Time

Now reserves and returns an instant from this store's clock.

Every write this store makes afterwards is stamped strictly later, which is what makes "the state before that change" expressible without sleeping:

before := store.Now()
store.UpsertNode(ctx, changed)
store.GetNode(graph.AsOf(ctx, before), id) // the old content

func (*GraphStore) PageRank

func (g *GraphStore) PageRank(ctx context.Context, iterations int, dampingFactor float64) ([]PageRankResult, error)

PageRank calculates PageRank scores for all nodes in the graph Optimized to load only topology (IDs and Edges) instead of full node objects.

func (*GraphStore) PredictEdges

func (g *GraphStore) PredictEdges(ctx context.Context, nodeID string, topK int) ([]EdgePrediction, error)

PredictEdges predicts potential edges using various methods

func (*GraphStore) PropertyCounts added in v2.93.0

func (g *GraphStore) PropertyCounts(ctx context.Context, key string) (map[string]PropertyCount, error)

PropertyCounts groups every node and edge by one top-level property.

Records that do not carry the property at all are counted under the empty string, for the reason NodeTypeCounts gives about untyped nodes: "nothing says" is a finding, and dropping it turns a caller's sum into a discrepancy it has to go and explain. It is usually the most important number in the result — a breakdown over the 3% of a shelf that was stamped looks exactly like a breakdown over all of it.

func (*GraphStore) PropertyCountsOfKnowledge added in v2.110.0

func (g *GraphStore) PropertyCountsOfKnowledge(ctx context.Context, key string) (map[string]PropertyCount, error)

PropertyCountsOfKnowledge is PropertyCounts over the records that are claims about the world, with the store's own filing left out.

The difference is not cosmetic. A Decision node carries a contract of its own — somebody signed the decision, so it is stamped verified — and counting it under Verified says the brain has more established facts in it than it has. The chunks and their has_chunk edges carry no contract at all and land under Untagged, saying the opposite. A tally built to answer "how well established is what this brain knows" has to be asked only about the things the brain knows; the filing is counted by BookkeepingCount, as itself.

func (*GraphStore) Purge added in v2.99.0

func (g *GraphStore) Purge(ctx context.Context, before time.Time, dryRun bool) (*PurgeReport, error)

Purge physically removes history rows that closed before `before`.

The only hard delete in this file, and the reason the rest of it can be additive: history is storage that grows with every correction and every retraction, and an operator has to be able to get it back. It is a write.

"Closed" is retracted_at when the row was retracted and valid_to when it was superseded; every history row has one of them by construction. Both are past belief, and an operator reclaiming space wants both — a purge that left every superseded version behind would reclaim almost nothing on a graph that is corrected more often than it is deleted from.

Purge never touches a live table, so nothing a current read can see is at risk. It refuses a zero cutoff rather than reading it as "everything": the zero time is what an unset field looks like, and a caller that forgot to set one must not thereby erase the whole record.

func (*GraphStore) RecordsWithProperties added in v2.93.0

func (g *GraphStore) RecordsWithProperties(ctx context.Context, q PropertyRecordQuery) ([]PropertyRecord, error)

RecordsWithProperties lists the nodes and edges matching a property query.

Ordered by id within each table and nodes before edges, so two reads of one graph agree and a caller paging with Limit sees a stable sequence. Limit is applied to each table and then to the merged result: a cap that returned only nodes because they sorted first would hide every matching edge, which on a shelf whose assertions are mostly edges is the whole answer.

func (*GraphStore) RefreshRDFSInferences added in v2.16.0

func (g *GraphStore) RefreshRDFSInferences(ctx context.Context) (*RDFSInferenceRefreshResult, error)

RefreshRDFSInferences recomputes and persists inferred triples using an RDFS-lite ruleset.

func (*GraphStore) RefreshRDFSInferencesIncremental added in v2.18.0

func (g *GraphStore) RefreshRDFSInferencesIncremental(ctx context.Context, changedTriples []RDFTriple) (*RDFSInferenceRefreshResult, error)

RefreshRDFSInferencesIncremental recomputes inferred triples only for the neighborhood affected by the supplied changed explicit triples.

func (*GraphStore) RetractEdgeAt added in v2.99.0

func (g *GraphStore) RetractEdgeAt(ctx context.Context, edgeID string, at time.Time) error

RetractEdgeAt retracts one edge as of `at`. DeleteEdge is this, now.

func (*GraphStore) RetractNodeAt added in v2.99.0

func (g *GraphStore) RetractNodeAt(ctx context.Context, nodeID string, at time.Time) error

RetractNodeAt retracts a node, and every edge touching it, as of `at`.

DeleteNode is this with `at` taken from the store's clock. The instant is a parameter because a retraction is a claim about when belief ended, and that is not always the moment somebody got round to running the delete: a fact discovered on Friday to have been wrong since Tuesday is retracted as of Tuesday, and every as-of read after Tuesday then agrees.

func (*GraphStore) RetractNodes added in v2.99.0

func (g *GraphStore) RetractNodes(ctx context.Context, nodeIDs []string) (nodes int, edges int, err error)

RetractNodes retracts many nodes in one transaction, reporting how many rows it found. Ids that match nothing are not an error — "not there" is not a failure to retract — so the count is what the caller should report.

func (*GraphStore) SPARQLMutates added in v2.91.0

func (g *GraphStore) SPARQLMutates(ctx context.Context, query string) bool

SPARQLMutates reports whether a query would change the graph.

It exists so authorization can tell a SPARQL read from a SPARQL write, and it is deliberately a thin wrapper over the executor's own parser rather than a second one. The alternative — a policy that recognises "INSERT" and "DELETE" by itself — has to agree with ExecuteSPARQL forever, and the failure when it stops agreeing is silent in the dangerous direction: an update the policy read as a query.

Without this the only safe classification is "every SPARQL call is a write", which costs a read-only key the whole query language, SELECT included.

A query that does not parse is reported as mutating. It is about to fail anyway, and the caller learns nothing from which error it gets.

func (*GraphStore) SaveRule added in v2.98.0

func (g *GraphStore) SaveRule(ctx context.Context, rule Rule, enabled bool) (*StoredRule, error)

SaveRule stores a rule under its id, replacing any rule already there.

func (*GraphStore) ShortestPath

func (g *GraphStore) ShortestPath(ctx context.Context, fromID, toID string) (*PathResult, error)

ShortestPath finds the shortest path between two nodes using BFS

func (*GraphStore) SimilarityInGraph

func (g *GraphStore) SimilarityInGraph(ctx context.Context, nodeID string, opts core.SearchOptions) ([]*HybridResult, error)

SimilarityInGraph finds nodes similar to a given node within the graph

func (*GraphStore) Subgraph

func (g *GraphStore) Subgraph(ctx context.Context, nodeIDs []string) (*GraphResult, error)

Subgraph extracts a subgraph containing specified nodes and their connections

func (*GraphStore) SyncDeletedNodeIDs added in v2.14.1

func (g *GraphStore) SyncDeletedNodeIDs(_ context.Context, nodeIDs []string)

SyncDeletedNodeIDs removes committed graph-node deletions from the optional HNSW index.

func (*GraphStore) SyncUpsertedNodes added in v2.14.1

func (g *GraphStore) SyncUpsertedNodes(_ context.Context, nodes []*GraphNode)

SyncUpsertedNodes updates the optional graph HNSW index after nodes were committed through ExecuteBatchTx.

func (*GraphStore) UpsertEdge

func (g *GraphStore) UpsertEdge(ctx context.Context, edge *GraphEdge) error

UpsertEdge inserts or updates an edge in the graph

func (*GraphStore) UpsertEdgesBatch

func (g *GraphStore) UpsertEdgesBatch(ctx context.Context, edges []*GraphEdge) (*BatchResult, error)

UpsertEdgesBatch inserts or updates multiple edges in a single transaction

func (*GraphStore) UpsertNamespace added in v2.16.0

func (g *GraphStore) UpsertNamespace(ctx context.Context, ns Namespace) error

UpsertNamespace stores or replaces one namespace mapping.

func (*GraphStore) UpsertNode

func (g *GraphStore) UpsertNode(ctx context.Context, node *GraphNode) error

UpsertNode inserts or updates a node in the graph

func (*GraphStore) UpsertNodesBatch

func (g *GraphStore) UpsertNodesBatch(ctx context.Context, nodes []*GraphNode) (*BatchResult, error)

UpsertNodesBatch inserts or updates multiple nodes in a single transaction

func (*GraphStore) UpsertTriple added in v2.16.0

func (g *GraphStore) UpsertTriple(ctx context.Context, triple *RDFTriple) error

UpsertTriple writes one RDF triple/quad and mirrors it into the property graph tables.

func (*GraphStore) UpsertTriplesBatch added in v2.16.0

func (g *GraphStore) UpsertTriplesBatch(ctx context.Context, triples []*RDFTriple) (*BatchResult, error)

UpsertTriplesBatch writes multiple RDF triples/quads.

func (*GraphStore) ValidateSHACL added in v2.18.0

func (g *GraphStore) ValidateSHACL(ctx context.Context, shapeTriples []RDFTriple) (*SHACLReport, error)

ValidateSHACL runs SHACL validation against the graph store using the provided shapes.

type HNSWGraphIndex

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

HNSWGraphIndex provides HNSW-accelerated graph searches

type HybridQuery

type HybridQuery struct {
	Vector          []float32     `json:"vector,omitempty"`
	StartNodeID     string        `json:"start_node_id,omitempty"`
	CenterNodes     []string      `json:"center_nodes,omitempty"`
	GraphFilter     *GraphFilter  `json:"graph_filter,omitempty"`
	TopK            int           `json:"top_k"`
	Threshold       float64       `json:"threshold,omitempty"`
	VectorThreshold float64       `json:"vector_threshold,omitempty"`
	TotalThreshold  float64       `json:"total_threshold,omitempty"`
	VectorWeight    float64       `json:"vector_weight"`
	GraphWeight     float64       `json:"graph_weight"`
	Weights         HybridWeights `json:"weights"`
}

HybridQuery represents a combined vector and graph query

type HybridResult

type HybridResult struct {
	Node          *GraphNode `json:"node"`
	VectorScore   float64    `json:"vector_score"`
	GraphScore    float64    `json:"graph_score"`
	CombinedScore float64    `json:"combined_score"`
	TotalScore    float64    `json:"total_score"`
	Path          []string   `json:"path,omitempty"` // Path from start node
	Distance      int        `json:"distance"`       // Graph distance from start
}

HybridResult represents a result from hybrid search

type HybridWeights

type HybridWeights struct {
	VectorWeight float64 `json:"vector_weight"` // Weight for vector similarity
	GraphWeight  float64 `json:"graph_weight"`  // Weight for graph proximity
	EdgeWeight   float64 `json:"edge_weight"`   // Weight for edge strength
}

HybridWeights defines the weights for hybrid scoring

type Interval added in v2.99.0

type Interval struct {
	From time.Time `json:"from,omitzero"`
	To   time.Time `json:"to,omitzero"`
}

Interval is a half-open span [From, To), matching how valid_from and valid_to are compared in SQL: a version that ends at the instant the next begins has no gap and no overlap.

A zero From is unbounded in the past and a zero To is unbounded in the future, which is what NULL means in those columns. The asymmetry is real — the same zero value means opposite things at the two ends — so the comparisons below never touch time.Time.Before directly.

func IntervalOf added in v2.99.0

func IntervalOf(validFrom, validTo time.Time) Interval

IntervalOf builds an interval from a row's validity columns.

func (Interval) Valid added in v2.99.0

func (i Interval) Valid() bool

Valid reports whether the interval spans anything. A zero-length or inverted span is not an interval Allen's relations are defined over.

type IntervalRelation added in v2.99.0

type IntervalRelation struct {
	// Subject is what both facts are about — the node both edges leave from.
	// Two intervals with nothing in common are not worth relating.
	Subject  string        `json:"subject"`
	A        string        `json:"a"`
	B        string        `json:"b"`
	Relation AllenRelation `json:"relation"`
	AFrom    time.Time     `json:"a_from,omitzero"`
	ATo      time.Time     `json:"a_to,omitzero"`
	BFrom    time.Time     `json:"b_from,omitzero"`
	BTo      time.Time     `json:"b_to,omitzero"`
}

IntervalRelation is one named pair, as a diff or a snapshot reports it.

func RelateEdges added in v2.99.0

func RelateEdges(edges []*GraphEdge, maxPairs int) []IntervalRelation

RelateEdges names the temporal relation between every pair of edges that share a subject.

Bounded by maxPairs because the pair count is quadratic in the edges per subject: a node with two hundred facts about it yields twenty thousand pairs, which is a tool response nobody reads and a model cannot use.

type Namespace added in v2.16.0

type Namespace struct {
	Prefix string `json:"prefix"`
	URI    string `json:"uri"`
}

Namespace represents a prefix to IRI mapping.

type NodeChange added in v2.99.0

type NodeChange struct {
	ID     string       `json:"id"`
	Kind   DiffKind     `json:"kind"`
	Before *NodeVersion `json:"before,omitempty"`
	After  *NodeVersion `json:"after,omitempty"`
}

NodeChange and EdgeChange carry both sides, so a reader sees what it said before as well as what it says now. Before is nil for an addition and After is nil for a retraction; a diff that reported only ids would send the caller back for two more reads per row.

type NodeLabel added in v2.86.0

type NodeLabel struct {
	ID       string `json:"id"`
	NodeType string `json:"node_type"`
	Content  string `json:"content"`
}

NodeLabel is a node's identity together with what it is called.

type NodeLabelQuery added in v2.86.0

type NodeLabelQuery struct {
	// IDPrefix keeps only nodes whose id starts with it. Node ids are
	// namespaced by what wrote them, so this is how a caller asks for one
	// writer's nodes without knowing how to recognise them from content.
	// Matched literally: % and _ in the prefix are not wildcards.
	IDPrefix string
	// MinContentLength drops nodes whose label is shorter than this, counted
	// in characters. 1 excludes the unlabelled.
	MinContentLength int
	// Limit caps the rows returned. 0 means no cap.
	Limit int
}

NodeLabelQuery narrows NodeLabels. A zero query returns every node.

type NodeVersion added in v2.99.0

type NodeVersion struct {
	ID         string    `json:"id"`
	Content    string    `json:"content,omitempty"`
	NodeType   string    `json:"node_type,omitempty"`
	Properties string    `json:"properties,omitempty"`
	ValidFrom  time.Time `json:"valid_from,omitzero"`
	ValidTo    time.Time `json:"valid_to,omitzero"`
}

NodeVersion is a node as it stood at one instant, without its vector.

The vector is left out on purpose: a diff of a thousand nodes would carry three megabytes of floats that answer nothing about what changed, and content is what changed.

func (NodeVersion) Interval added in v2.99.0

func (v NodeVersion) Interval() Interval

Interval is this version's validity, for Relate.

type PageRankResult

type PageRankResult struct {
	NodeID string  `json:"node_id"`
	Score  float64 `json:"score"`
}

PageRankResult represents the PageRank score for a node

type PathResult

type PathResult struct {
	Nodes    []*GraphNode `json:"nodes"`
	Edges    []*GraphEdge `json:"edges"`
	Distance int          `json:"distance"`
	Weight   float64      `json:"weight"`
}

PathResult represents a path in the graph

type PropertyCount added in v2.93.0

type PropertyCount struct {
	Nodes int `json:"nodes"`
	Edges int `json:"edges"`
}

PropertyCount is how many nodes and how many edges carry one value.

Kept apart rather than summed because the two are not interchangeable to a reader: an edge is an assertion about two things and a node is one thing, so "40 records" over a graph of 4 nodes and 36 edges describes a different graph than the reverse, and a caller that wants the total can add them.

type PropertyKeyUsage added in v2.94.0

type PropertyKeyUsage struct {
	NodeType string `json:"node_type"`
	Key      string `json:"key"`
	Records  int    `json:"records"`
	Distinct int    `json:"distinct_values"`
}

PropertyKeyUsage is one property key as it appears on the nodes of one type: how many of them carry it, and how many distinct values they carry.

Distinct is the field that earns the type. Coverage alone says a key is present; only distinctness separates a key that identifies a record from one that classifies it, which on a store of 4,000 nodes is the difference between `name` and `type` and is the only evidence data can offer about identity at all.

type PropertyRecord added in v2.93.0

type PropertyRecord struct {
	ID string `json:"id"`
	// Edge distinguishes the two without a caller having to guess from the
	// shape of the id.
	Edge bool `json:"edge"`
	// Type is node_type or edge_type.
	Type string `json:"type"`
	// Content is a node's label. Edges have none — they are named by their
	// type and their ends.
	Content string `json:"content,omitempty"`
	// From and To are an edge's ends, empty on a node. Ids rather than labels:
	// resolving them costs a second query per row, and a caller that wants the
	// labels has GetNodesBatch.
	From string `json:"from,omitempty"`
	To   string `json:"to,omitempty"`
	// Properties holds exactly the keys asked for. A key the record does not
	// carry is absent rather than empty, so "nothing says" stays
	// distinguishable from "says the empty string".
	Properties map[string]string `json:"properties,omitempty"`
}

PropertyRecord is one node or edge, with the properties the caller asked for.

type PropertyRecordQuery added in v2.93.0

type PropertyRecordQuery struct {
	// Where narrows to records carrying these properties. Keys are ANDed and
	// the values within one key are ORed, which is the shape the questions
	// actually take: "held or refused, from this import".
	Where map[string][]string
	// Fetch names the properties to return on each record. Empty returns the
	// keys in Where, which is the common case and saves repeating them.
	Fetch []string
	// Limit caps the rows. 0 means no cap.
	Limit int
}

PropertyRecordQuery narrows RecordsWithProperties. A zero query is refused rather than returning the whole graph: an unfiltered read of both tables is never what a caller of this API meant, and returning it makes the mistake look like it worked until the graph is large.

type PurgeReport added in v2.99.0

type PurgeReport struct {
	Nodes  int       `json:"nodes"`
	Edges  int       `json:"edges"`
	Before time.Time `json:"before"`
	DryRun bool      `json:"dry_run,omitempty"`
}

PurgeReport is what Purge reclaimed.

type RDFFormat added in v2.16.0

type RDFFormat string

RDFFormat represents a supported RDF serialization format.

const (
	// RDFFormatNTriples exports triples in N-Triples syntax.
	RDFFormatNTriples RDFFormat = "ntriples"
	// RDFFormatNQuads exports statements in N-Quads syntax when graph names are present.
	RDFFormatNQuads RDFFormat = "nquads"
	// RDFFormatTurtle exports statements in a Turtle-like syntax with prefixes when possible.
	RDFFormatTurtle RDFFormat = "turtle"
	// RDFFormatTriG exports quads in TriG syntax with graph blocks.
	RDFFormatTriG RDFFormat = "trig"
)

type RDFSInferenceExplanation added in v2.16.0

type RDFSInferenceExplanation struct {
	Triple           RDFTriple `json:"triple"`
	Explicit         bool      `json:"explicit"`
	Rule             string    `json:"rule,omitempty"`
	SupportTripleIDs []string  `json:"support_triple_ids,omitempty"`
}

RDFSInferenceExplanation returns provenance information for one triple.

type RDFSInferenceMatchExplanation added in v2.18.0

type RDFSInferenceMatchExplanation struct {
	Explanation RDFSInferenceExplanation  `json:"explanation"`
	Trace       []RDFSInferenceTraceEntry `json:"trace,omitempty"`
}

RDFSInferenceMatchExplanation combines explanation and optional trace for one matched triple.

type RDFSInferenceRefreshResult added in v2.16.0

type RDFSInferenceRefreshResult struct {
	ExplicitCount         int  `json:"explicit_count"`
	InferredCount         int  `json:"inferred_count"`
	Incremental           bool `json:"incremental,omitempty"`
	AffectedExplicitCount int  `json:"affected_explicit_count,omitempty"`
	RemovedInferredCount  int  `json:"removed_inferred_count,omitempty"`
}

RDFSInferenceRefreshResult summarizes a refresh of inferred triples.

type RDFSInferenceSummary added in v2.18.0

type RDFSInferenceSummary struct {
	ExplicitCount int            `json:"explicit_count"`
	InferredCount int            `json:"inferred_count"`
	Rules         map[string]int `json:"rules,omitempty"`
}

RDFSInferenceSummary provides persisted inference counts and rule breakdowns.

type RDFSInferenceTraceEntry added in v2.16.0

type RDFSInferenceTraceEntry struct {
	TripleID       string                   `json:"triple_id"`
	ParentTripleID string                   `json:"parent_triple_id,omitempty"`
	Depth          int                      `json:"depth"`
	Explanation    RDFSInferenceExplanation `json:"explanation"`
	Truncated      bool                     `json:"truncated,omitempty"`
}

RDFSInferenceTraceEntry is one flattened node in an explanation trace.

type RDFTerm added in v2.16.0

type RDFTerm struct {
	Kind     string `json:"kind"`
	Value    string `json:"value"`
	Datatype string `json:"datatype,omitempty"`
	Language string `json:"language,omitempty"`
}

RDFTerm represents one RDF term.

func NewBlankNode added in v2.16.0

func NewBlankNode(value string) RDFTerm

NewBlankNode creates a blank node term.

func NewIRI added in v2.16.0

func NewIRI(value string) RDFTerm

NewIRI creates an IRI term.

func NewLangLiteral added in v2.16.0

func NewLangLiteral(value, language string) RDFTerm

NewLangLiteral creates a language-tagged literal term.

func NewLiteral added in v2.16.0

func NewLiteral(value string) RDFTerm

NewLiteral creates a plain literal term.

func NewTypedLiteral added in v2.16.0

func NewTypedLiteral(value, datatype string) RDFTerm

NewTypedLiteral creates a typed literal term.

func (RDFTerm) String added in v2.16.0

func (t RDFTerm) String() string

String renders the term using RDF-compatible syntax.

type RDFTriple added in v2.16.0

type RDFTriple struct {
	ID         string   `json:"id,omitempty"`
	Subject    RDFTerm  `json:"subject"`
	Predicate  RDFTerm  `json:"predicate"`
	Object     RDFTerm  `json:"object"`
	Graph      *RDFTerm `json:"graph,omitempty"`
	Inferred   bool     `json:"inferred,omitempty"`
	Rule       string   `json:"rule,omitempty"`
	SupportIDs []string `json:"support_ids,omitempty"`

	// Provenance is carried onto the edge this triple becomes, so a triple
	// written by a machine can say which machine, under whose authority, and
	// on what grade. Nothing here is interpreted; it is the writer's statement
	// about its own output, and keys that collide with the triple's own
	// description are dropped rather than allowed to overwrite it.
	//
	// The RDF store had no such field, which meant every triple an import
	// wrote was anonymous: the row it came from, the plan that was signed to
	// read it, and the operator who signed it were all recoverable from the
	// ledger and from nothing on the fact itself.
	Provenance map[string]string `json:"provenance,omitempty"`
}

RDFTriple represents one RDF triple or quad when Graph is set.

func (RDFTriple) String added in v2.16.0

func (t RDFTriple) String() string

String renders the triple/quad using RDF syntax.

type ReadOptions added in v2.99.0

type ReadOptions struct {
	// AsOf reads the graph as it stood at this instant. The zero time means
	// now, which is what every caller before this field got and still gets.
	AsOf time.Time
}

ReadOptions is how much of the past a read is allowed to see.

Carried on the context rather than passed as an argument, which is the unusual choice and a deliberate one. The alternative is a parameter on every read, and the read surface here is not flat: Neighbors calls GetEdges, which calls nothing; ShortestPath calls GetEdges and getEdgeByID; ExpandGraph in pkg/cortexdb calls Neighbors; HybridSearch calls its own scan. Threading a parameter means changing the signature of every one of them — breaking every caller in and outside this module — or shipping an As-Of twin of each and letting the two drift. An as-of read is also not really an argument to one query: it is the epoch the whole traversal happens in, and a traversal that read some hops at one instant and some at another would produce a graph that never existed.

The risk of an ambient setting is that it leaks into a write. That is closed rather than documented away: every write path calls errIfAsOf and refuses.

func ReadOptionsFrom added in v2.99.0

func ReadOptionsFrom(ctx context.Context) ReadOptions

ReadOptionsFrom reads back what WithReadOptions put on the context. The zero value — read now — is returned for a context that carries nothing.

func (ReadOptions) IsAsOf added in v2.99.0

func (r ReadOptions) IsAsOf() bool

AsOf reports whether these options ask for a past read.

type Rule added in v2.98.0

type Rule struct {
	// ID is the stable identity written into every edge this rule derives, and
	// the key it persists under. Required.
	ID string `json:"id"`
	// Name is a human label. Optional; it never participates in matching.
	Name string `json:"name,omitempty"`
	// When are the premises, matched in order. Each one is a graph edge
	// pattern; a variable bound by an earlier premise constrains the later
	// ones.
	When []Atom `json:"when"`
	// Then is the conclusion. Every variable in it must be bound by some
	// premise — see Validate, which is what stops a rule deriving edges
	// between nodes it never looked at.
	Then Atom `json:"then"`
	// Confidence multiplies into every derived edge's confidence. Zero means
	// 1.0, so a rule that says nothing about confidence does not silently
	// erase the premises'.
	Confidence float64 `json:"confidence,omitempty"`
	// Note is free text carried alongside the rule for whoever reads it later.
	Note string `json:"note,omitempty"`
	// Weight overrides the derived edge weight. Zero means the mean of the
	// premise weights, which for a two-premise rule is the average the
	// original two-hop inference wrote.
	Weight float64 `json:"weight,omitempty"`
	// Metadata is written onto every derived edge, except for the provenance
	// keys the engine owns — those cannot be overridden, because an edge whose
	// rule_id says something other than the rule that derived it is an edge
	// that lies to inference_explain.
	Metadata map[string]string `json:"metadata,omitempty"`
}

Rule is a Horn clause over graph edges: derive Then whenever every atom in When can be matched with one consistent set of variable bindings.

func ParseRule added in v2.98.0

func ParseRule(id, text string) (Rule, error)

ParseRule parses the textual form and gives the result an ID, which is the common case: a caller with a rule to save has both.

func ParseRuleText added in v2.98.0

func ParseRuleText(text string) (Rule, error)

ParseRuleText parses the textual form into a Rule with When and Then filled in. ID, Name, Confidence and Metadata are the caller's to set: they are bookkeeping about the rule, not part of the logic it states.

func (Rule) Text added in v2.98.0

func (r Rule) Text() string

Text renders a rule back into the textual form ParseRuleText accepts, so a rule built in Go and a rule typed by a person print the same.

func (Rule) Validate added in v2.98.0

func (r Rule) Validate() error

Validate checks the rule is well formed and safe: an unsafe rule — one whose conclusion carries a variable no premise binds — would have to invent nodes to fire, so it is rejected here rather than quietly matching nothing.

type RuleApplyResult added in v2.98.0

type RuleApplyResult struct {
	// Iterations is how many chaining rounds actually ran.
	Iterations int `json:"iterations"`
	// CandidateEdges is how many stored edges took part as facts.
	CandidateEdges int `json:"candidate_edges"`
	// CreatedEdgeIDs are the edges this run wrote, sorted. On a dry run they
	// are the edges it would have written.
	CreatedEdgeIDs []string `json:"created_edge_ids,omitempty"`
	// UnchangedEdgeIDs are edges this rule set re-derived that were already
	// stored by the same rule. Reported rather than rewritten, which is what
	// makes a second run of the same rules a no-op.
	UnchangedEdgeIDs []string `json:"unchanged_edge_ids,omitempty"`
	// Edges are the derived edges in CreatedEdgeIDs order, with the provenance
	// they carry.
	Edges []*GraphEdge `json:"edges,omitempty"`
	// UnresolvedTerms are literal terms in the rules that match no stored node,
	// so the rules mentioning them could never fire. Reported because a rule
	// that silently matches nothing looks exactly like a rule that is simply
	// not true of this graph.
	UnresolvedTerms []string `json:"unresolved_terms,omitempty"`
	// CapHit and CapReason say the run stopped at a cap rather than at a
	// fixpoint. When they are set nothing was written and ApplyRules returned
	// ErrRuleCapExceeded.
	CapHit    bool   `json:"cap_hit,omitempty"`
	CapReason string `json:"cap_reason,omitempty"`
	// DryRun echoes the option, so a caller reading only the result can tell
	// whether the edges it lists are in the store.
	DryRun bool `json:"dry_run,omitempty"`
}

RuleApplyResult reports what one forward-chaining run did.

type RuleEdgeExplanation added in v2.98.0

type RuleEdgeExplanation struct {
	EdgeID     string  `json:"edge_id"`
	EdgeType   string  `json:"edge_type,omitempty"`
	FromNodeID string  `json:"from_node_id,omitempty"`
	ToNodeID   string  `json:"to_node_id,omitempty"`
	Inferred   bool    `json:"inferred"`
	Provenance string  `json:"provenance,omitempty"`
	RuleID     string  `json:"rule_id,omitempty"`
	RuleText   string  `json:"rule_text,omitempty"`
	Confidence float64 `json:"confidence,omitempty"`
	// SupportEdgeIDs are the exact premise edges, in the order of the rule's
	// premises.
	SupportEdgeIDs []string `json:"support_edge_ids,omitempty"`
	// Missing marks a premise edge that is no longer in the graph — somebody
	// deleted the evidence without retracting the conclusion.
	Missing bool `json:"missing,omitempty"`
}

RuleEdgeExplanation says why one edge is in the graph.

An explicit edge explains itself: Inferred is false and it names no support. A derived edge names the rule that derived it — including the rule text, which is stored on the edge rather than looked up, so an edge stays explicable after its rule is deleted or edited — and the exact premise edges under it.

It is deliberately one level deep. The chain is returned separately, by ExplainEdgeTrace, as a flat list: a self-referential struct cannot be given a JSON schema, so a nested explanation could never have crossed the MCP boundary most callers reach this through.

type RuleEdgeTraceEntry added in v2.98.0

type RuleEdgeTraceEntry struct {
	EdgeID       string              `json:"edge_id"`
	ParentEdgeID string              `json:"parent_edge_id,omitempty"`
	Depth        int                 `json:"depth"`
	Explanation  RuleEdgeExplanation `json:"explanation"`
	// Truncated marks an inferred edge whose own premises were not followed,
	// because the depth ran out or because the chain looped. Without it a leaf
	// of a truncated trace reads as an explicit edge.
	Truncated bool `json:"truncated,omitempty"`
}

RuleEdgeTraceEntry is one node of a flattened derivation, in preorder: the edge asked about at depth 0, then its premises, then theirs.

type RuleOptions added in v2.98.0

type RuleOptions struct {
	// DocumentID scopes which edges may take part and is stamped onto what is
	// derived. Empty means the whole graph, and derived edges inherit a
	// document only when every premise agrees on one.
	DocumentID string
	// MaxIterations caps the chaining rounds. Zero means DefaultRuleMaxIterations.
	MaxIterations int
	// MaxDerived caps how many edges one run may derive. Zero means
	// DefaultRuleMaxDerived.
	MaxDerived int
	// DryRun computes the derivation and writes nothing. The result carries the
	// edges it would have written.
	DryRun bool
	// Validate, when set, is given the derived edges before they are written
	// and can refuse them. It exists because the ontology that decides whether
	// a relation is legal lives a layer up, and derived edges have to pass the
	// same gate hand-written ones do.
	Validate func(ctx context.Context, edges []*GraphEdge) error
}

RuleOptions configures one forward-chaining run.

type RuleParseError added in v2.98.0

type RuleParseError struct {
	// Offset is a 0-based byte offset into the source text.
	Offset int
	// Message says what was expected there.
	Message string
	// Source is the text that failed to parse.
	Source string
}

RuleParseError names where parsing stopped. A rule is usually typed by hand and usually wrong on the first try, so "expected ')'" without a position is most of an error message missing.

func (*RuleParseError) Error added in v2.98.0

func (e *RuleParseError) Error() string

type SHACLReport added in v2.18.0

type SHACLReport struct {
	Conforms bool                    `json:"conforms"`
	Results  []SHACLValidationResult `json:"results,omitempty"`
}

SHACLReport contains the outcome of SHACL validation.

type SHACLValidationResult added in v2.18.0

type SHACLValidationResult struct {
	FocusNode RDFTerm `json:"focus_node"`
	Path      RDFTerm `json:"path"`
	Value     RDFTerm `json:"value,omitempty"`
	Message   string  `json:"message"`
	Severity  string  `json:"severity"`
	Source    RDFTerm `json:"source_shape"`
}

SHACLValidationResult represents a single constraint violation.

type SPARQLResult added in v2.16.0

type SPARQLResult struct {
	QueryType string               `json:"query_type"`
	Vars      []string             `json:"vars,omitempty"`
	Bindings  []map[string]RDFTerm `json:"bindings,omitempty"`
	Triples   []RDFTriple          `json:"triples,omitempty"`
	Boolean   bool                 `json:"boolean,omitempty"`
	Count     int                  `json:"count"`
}

SPARQLResult contains the result of executing a SPARQL query.

type SimpleHNSW

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

SimpleHNSW implements a simplified HNSW-like index for vector search

func NewSimpleHNSW

func NewSimpleHNSW(dimensions int, maxConns int) *SimpleHNSW

NewSimpleHNSW creates a new simplified HNSW index

func (*SimpleHNSW) Add

func (h *SimpleHNSW) Add(id string, vector []float32) error

Add inserts a vector into the HNSW index

func (*SimpleHNSW) Remove

func (h *SimpleHNSW) Remove(nodeID string)

Remove removes a node from the index

func (*SimpleHNSW) Search

func (h *SimpleHNSW) Search(query []float32, k int) []searchCandidate

Search performs HNSW search

type StoredRule added in v2.98.0

type StoredRule struct {
	Rule
	// Text is the rule rendered into the textual form, stored alongside the
	// structured body so a human reading the table sees the rule.
	Text      string    `json:"text"`
	Enabled   bool      `json:"enabled"`
	CreatedAt time.Time `json:"created_at"`
	UpdatedAt time.Time `json:"updated_at"`
}

StoredRule is a rule as persisted, with the bookkeeping the store adds.

type TraversalOptions

type TraversalOptions struct {
	MaxDepth  int      `json:"max_depth"`
	EdgeTypes []string `json:"edge_types,omitempty"`
	NodeTypes []string `json:"node_types,omitempty"`
	Direction string   `json:"direction"` // "out", "in", "both"
	Limit     int      `json:"limit"`
}

TraversalOptions defines options for graph traversal

type TriplePattern added in v2.16.0

type TriplePattern struct {
	Subject   *RDFTerm `json:"subject,omitempty"`
	Predicate *RDFTerm `json:"predicate,omitempty"`
	Object    *RDFTerm `json:"object,omitempty"`
	Graph     *RDFTerm `json:"graph,omitempty"`
	Inferred  *bool    `json:"inferred,omitempty"`
	Limit     int      `json:"limit,omitempty"`
}

TriplePattern filters triple lookup operations. Nil fields behave as wildcards.

Jump to

Keyboard shortcuts

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