acropora

package module
v0.1.2 Latest Latest
Warning

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

Go to latest
Published: Mar 30, 2026 License: Apache-2.0 Imports: 18 Imported by: 0

README

Acropora Logo

Go Reference Go Report Card Version

Acropora is an alpha-stage Go library for storing directional subject–predicate–object triples on PostgreSQL. It provides a small, explicit persistence layer for semantically constrained graph-like data without the overhead of a dedicated graph database.

[!CAUTION] Status: Alpha Version. Acropora is currently unreleased and under active development. APIs are subject to change.

Core Concepts

Acropora is designed for teams that need more structure than a simple JSONB column but want to stay within the reliable PostgreSQL ecosystem.

  • Ontology First: Define your domain model (entities, predicates, and allowed triple patterns) before persisting data.
  • Versioning: Ontologies are explicitly versioned and hashed, ensuring data integrity across schema evolutions.
  • Canonicalization: Entities are automatically canonicalized (normalized) to handle variations in naming (e.g., "John Doe" vs "john doe").
  • Referential Integrity: Unlike raw JSONB, Acropora enforces that triples must follow the patterns defined in your ontology.
  • Entity Aliasing: Support for linking multiple entity aliases to a single canonical entity.

Up and Running

Prerequisites

  • Go 1.21+
  • PostgreSQL 13+

Installation

go get github.com/simonmittag/acropora

Initializing the Database

Acropora manages its own internal schema migrations using Goose.

import (
    "context"
    "database/sql"
    "github.com/simonmittag/acropora"
    _ "github.com/lib/pq"
)

func main() {
    ctx := context.Background()
    sqlDB, _ := sql.Open("postgres", "postgres://localhost/mydb?sslmode=disable")
    
    db, err := acropora.New(ctx, sqlDB, acropora.Options{})
    if err != nil {
        panic(err)
    }
}

Examples

1. Seeding an Ontology

Define the rules for your graph. In this example, we define that a Person can work_at a Company.

def := acropora.Definition{
    Entities: []acropora.EntityDefinition{
        {Type: "Person"},
        {Type: "Company"},
    },
    Predicates: []acropora.PredicateDefinition{
        {Type: "works_at"},
    },
    Triples: []acropora.TripleDefinition{
        {
            Subject:   &acropora.EntityDefinition{Type: "Person"},
            Predicate: &acropora.PredicateDefinition{Type: "works_at"},
            Object:    &acropora.EntityDefinition{Type: "Company"},
        },
    },
}

version, _ := db.SeedOntology(ctx, sqlDB, def, acropora.SeedOptions{Slug: "v1"})

2. Working with a Session

Once the ontology is seeded, use a Session to interact with your data.

session := db.NewSession(version)

// Insert Entities
john, _ := session.MatchEntity(ctx, acropora.Entity{
    EntityDefinition: acropora.EntityDefinition{Type: "Person"},
    RawName: "John Doe",
})

jane, _ := session.MatchEntity(ctx, acropora.Entity{
    EntityDefinition: acropora.EntityDefinition{Type: "Person"},
    RawName: "Jane Smith",
})

company, _ := session.MatchEntity(ctx, acropora.Entity{
    EntityDefinition: acropora.EntityDefinition{Type: "Company"},
    RawName: "Acme Corp",
})

// Create Triples (Subject - Predicate - Object)
predicate, _ := session.MatchPredicate(ctx, acropora.Predicate{
    PredicateDefinition: acropora.PredicateDefinition{
        Type:      "works_at",
        ValidFrom: time.Now(),
    },
})

session.MatchTriple(ctx, acropora.Triple{
    SubjectEntityID: john.ID,
    PredicateID:     predicate.ID,
    ObjectEntityID:  company.ID,
})

session.MatchTriple(ctx, acropora.Triple{
    SubjectEntityID: jane.ID,
    PredicateID:     predicate.ID,
    ObjectEntityID:  company.ID,
})

// Get Entity Neighbours (one-hop relationships)
// Acme Corp will have two incoming 'works_at' neighbours: John and Jane.
neighbours, _ := session.GetEntityNeighbours(ctx, company.ID)

License

Acropora is licensed under the Apache License, Version 2.0. See LICENSE for the full license text.

Documentation

Index

Constants

View Source
const (
	OptionID   = "id"
	OptionHash = "hash"
	OptionSlug = "slug"
)

Variables

View Source
var Version = "dev"

Version is the current version of the acropora library. It is injected at build time via ldflags.

Functions

func MergeEntityMetadata

func MergeEntityMetadata(canonical json.RawMessage, aliases ...json.RawMessage) (json.RawMessage, error)

MergeEntityMetadata merges metadata from multiple entities, with canonical winning on conflict.

Types

type DB

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

DB is a wrapper type for our sql.DB handle.

func New

func New(ctx context.Context, dbConn *sql.DB, opts Options) (*DB, error)

New creates a new Acropora DB wrapper, verifies the connection, and runs migrations.

func (*DB) BeginTx

func (d *DB) BeginTx(ctx context.Context, opts *sql.TxOptions) (*sql.Tx, error)

BeginTx exposes transaction creation on the underlying DB handle.

func (*DB) GetOntologyVersion

func (d *DB) GetOntologyVersion(ctx context.Context, opts GetOntologyVersionOptions) (OntologyVersion, error)

GetOntologyVersion returns an ontology version based on the provided options. If options are empty, it returns the latest (default) version. Supported keys: id, hash, slug. Only one key can be provided at a time.

func (*DB) ListOntologyVersions

func (d *DB) ListOntologyVersions(ctx context.Context) ([]OntologyVersion, error)

ListOntologyVersions returns all ontology versions, sorted by most recent first.

func (*DB) NewSession

func (d *DB) NewSession(version OntologyVersion) *Session

NewSession creates a new session bound to exactly one ontology version.

func (*DB) RawDB

func (d *DB) RawDB() *sql.DB

RawDB returns the underlying *sql.DB handle.

func (*DB) SeedOntology

func (d *DB) SeedOntology(ctx context.Context, db *sql.DB, def Definition, opts SeedOptions) (OntologyVersion, error)

SeedOntology validates and writes a new ontology version to the database.

type Definition

type Definition struct {
	Entities   []EntityDefinition
	Predicates []PredicateDefinition
	Triples    []TripleDefinition
}

Definition is a high-level ontology definition.

type Direction

type Direction string

Direction represents the direction of a relationship.

const (
	DirectionIncoming Direction = "incoming"
	DirectionOutgoing Direction = "outgoing"
)

type Entity

type Entity struct {
	Persistable
	EntityDefinition
	RawName       string
	CanonicalName string
}

Entity represents a persisted entity (either Ontology or Runtime).

type EntityAlias

type EntityAlias struct {
	Persistable
	AliasEntityID     string
	CanonicalEntityID string
	Metadata          json.RawMessage
}

EntityAlias represents a runtime link between two entities.

type EntityDefinition

type EntityDefinition struct {
	Type     string
	Metadata json.RawMessage
}

EntityDefinition represents the core properties of an entity.

type GetOntologyVersionOptions

type GetOntologyVersionOptions map[string]string

GetOntologyVersionOptions provides configuration for fetching an ontology version.

type Neighbour

type Neighbour struct {
	TripleID           string
	Direction          Direction
	PredicateType      string
	PredicateMetadata  json.RawMessage
	PredicateValidFrom time.Time
	PredicateValidTo   time.Time
	EntityID           string
	EntityType         string
	CanonicalName      string
	Metadata           json.RawMessage
}

Neighbour represents a flattened read model for a neighbouring entity.

type OntologySeeder

type OntologySeeder interface {
	SeedOntology(ctx context.Context, db *sql.DB, def Definition, opts SeedOptions) (OntologyVersion, error)
	ListOntologyVersions(ctx context.Context) ([]OntologyVersion, error)
	GetOntologyVersion(ctx context.Context, opts GetOntologyVersionOptions) (OntologyVersion, error)
}

OntologySeeder is the interface for seeding an ontology.

type OntologyVersion

type OntologyVersion struct {
	Persistable
	Slug string
	Hash string
}

OntologyVersion represents a specific version of the ontology.

type Options

type Options struct {
	TablePrefix string
}

Options allows configuring the Acropora DB wrapper.

type Persistable

type Persistable struct {
	ID                string
	OntologyVersionID string
	CreatedAt         time.Time
	UpdatedAt         time.Time
}

Persistable contains common fields for all database-backed entities.

type Predicate

type Predicate struct {
	Persistable
	PredicateDefinition
	ValidFrom time.Time
	ValidTo   time.Time
	Metadata  json.RawMessage
}

Predicate represents a persisted predicate (either Ontology or Runtime).

type PredicateDefinition

type PredicateDefinition struct {
	Type     string
	Metadata json.RawMessage
}

PredicateDefinition represents the core properties of a predicate.

type SeedOptions

type SeedOptions struct {
	Slug string
}

SeedOptions provides configuration for seeding an ontology.

type Session

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

Session is a version-bound runtime session.

func (*Session) GetAliasGroupEntityIDs

func (s *Session) GetAliasGroupEntityIDs(ctx context.Context, entityID string) ([]string, string, error)

GetAliasGroupEntityIDs returns all entity IDs in the alias group.

func (*Session) GetAntiAliasedEntityID

func (s *Session) GetAntiAliasedEntityID(ctx context.Context, entityID string) (string, error)

GetAntiAliasedEntityID returns the canonical root ID for an entity.

func (*Session) GetEntityByID

func (s *Session) GetEntityByID(ctx context.Context, id string) (Entity, error)

GetEntityByID fetches an entity by its ID, scoped to the session's ontology version.

func (*Session) GetEntityByRawName

func (s *Session) GetEntityByRawName(ctx context.Context, rawName string) (Entity, error)

GetEntityByRawName fetches an entity by its raw name, applying conservative canonicalization and anti-alias resolution.

func (*Session) GetEntityNeighbours

func (s *Session) GetEntityNeighbours(ctx context.Context, entityID string) ([]Neighbour, error)

GetEntityNeighbours returns all one-hop neighbours of an entity, expanding across the alias group.

func (*Session) GetOutgoingTriples

func (s *Session) GetOutgoingTriples(ctx context.Context, entityID string) ([]Triple, error)

GetOutgoingTriples returns all triples where subject_entity_id matches (expanded by alias group), scoped to the session's ontology version, and normalized to the canonical root.

func (*Session) GetPredicateByID

func (s *Session) GetPredicateByID(ctx context.Context, id string) (Predicate, error)

GetPredicateByID fetches a predicate by its ID, scoped to the session's ontology version.

func (*Session) GetTripleByID

func (s *Session) GetTripleByID(ctx context.Context, id string) (Triple, error)

GetTripleByID fetches a triple by its ID, scoped to the session's ontology version.

func (*Session) LinkEntityAlias

func (s *Session) LinkEntityAlias(ctx context.Context, aliasEntityID, canonicalEntityID string, metadata json.RawMessage) (EntityAlias, error)

LinkEntityAlias links an alias entity to a canonical root entity.

func (*Session) MatchEntity

func (s *Session) MatchEntity(ctx context.Context, entity Entity) (Entity, error)

MatchEntity canonicalizes the candidate entity, attempts to match an existing canonical entity in the current session ontology version, and inserts a new canonical entity if no match is found.

This method provides identity-aware entity materialization. It first normalizes the input entity's name and checks the session's ontology version for any existing entity with the same canonical name or linked via an alias. If a match is found, the existing canonical entity is returned. Otherwise, a new entity is created and inserted into the runtime after validating its type against the ontology.

MatchEntity is the primary public entrypoint for ensuring an entity exists within a session's context without creating duplicate entries for the same real-world identity.

func (*Session) MatchPredicate

func (s *Session) MatchPredicate(ctx context.Context, predicate Predicate) (Predicate, error)

MatchPredicate canonicalizes the candidate predicate, attempts to match an existing canonical predicate in the current session ontology version, and inserts a new canonical predicate if no match is found.

This method provides identity-aware predicate materialization. It first normalizes the input predicate's temporal window and metadata, and checks the session's ontology version for any existing predicate with the same identity hash. If a match is found, the existing canonical predicate is returned. Otherwise, a new predicate is created and inserted into the runtime after validating its type against the ontology.

MatchPredicate is the primary public entrypoint for ensuring a predicate exists within a session's context without creating duplicate entries for the same real-world identity.

func (*Session) MatchTriple

func (s *Session) MatchTriple(ctx context.Context, triple Triple) (Triple, error)

MatchTriple canonicalizes the subject and object entities, attempts to match an existing triple in the current session ontology version, and inserts a new triple if no match is found.

This method provides identity-aware triple materialization. It first resolves any subject or object aliases to their canonical root entities and checks if a triple with the same subject, predicate, and object already exists in the current session's ontology version. If a match is found, the existing triple is returned. Otherwise, a new triple is created and inserted into the runtime after validating the relationship against the ontology.

MatchTriple is the primary public entrypoint for ensuring a triple exists within a session's context without creating duplicate entries for the same semantic relationship.

type Triple

type Triple struct {
	Persistable
	SubjectEntityID string
	PredicateID     string
	ObjectEntityID  string
}

Triple represents a persisted triple (either Ontology or Runtime).

type TripleDefinition

type TripleDefinition struct {
	Subject   EntityDefinition
	Predicate PredicateDefinition
	Object    EntityDefinition
}

TripleDefinition represents the core properties of a triple definition.

Directories

Path Synopsis
internal
db

Jump to

Keyboard shortcuts

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