den

package module
v0.17.1 Latest Latest
Warning

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

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

README

Den

Go gophers organizing documents in their den
"Every burrow needs a den — a place to store what matters and find it again when you need it."

CI Release Go Version Go Report Card License Docs

An ODM for Go with two storage backends — SQLite and PostgreSQL. Same API, your choice of engine.

Each Go struct you register is a document, stored as a JSONB row in a SQL table that Den calls a collection. The SQL schema is one table per type with a JSONB data column plus a small set of secondary indexes Den maintains for you. You query collections with a fluent builder, relate them with typed links, and run it all in transactions. The SQLite backend compiles into your binary with no external dependencies. The PostgreSQL backend connects to your existing database. Switch between them by changing one line.

[!NOTE] Den is a document store, not a relational database. It does not support SQL, JOINs, or schema migrations in the traditional sense. If you need relational modeling, use Bun or GORM instead.

Features

  • Two backends, one API — SQLite (embedded, pure Go, no CGO) and PostgreSQL (server-based, JSONB + GIN indexes)
  • Chainable QuerySetNewQuery[T](db).Where(...).Sort(...).Limit(n).All(ctx) with lazy evaluation
  • Range iterationIter() returns iter.Seq2[*T, error] for memory-efficient streaming with Go's range
  • Typed relationsLink[T] for one-to-one, []Link[T] for one-to-many, with cascade write/delete and eager/lazy fetch
  • Back-referencesQuerySet.BackLinks finds all documents referencing a given target
  • Native aggregationAvg, Sum, Min, Max pushed down to SQL; GroupBy and Project for analytics
  • Full-text search — FTS5 for SQLite, tsvector for PostgreSQL, same Search() API
  • Lifecycle hooks — BeforeInsert, AfterUpdate, Validate, and more — interfaces on your struct, no registration
  • Change tracking — opt-in via Tracked: IsChanged, GetChanges, Revert with byte-level snapshots
  • Soft delete — embed SoftDelete alongside Base, automatic query filtering, HardDelete for permanent removal
  • Attachments & storage — embed Attachment, install a den.Storage backend once, let the hard-delete cascade clean bytes automatically
  • Optimistic concurrency — revision-based conflict detection with ErrRevisionConflict
  • TransactionsRunInTransaction with panic-safe rollback
  • Migrations — registry-based, each migration runs atomically in a transaction
  • Struct tag validationvalidate:"required,email" tags via go-playground/validator, always-on; no opt-in, no bypass from inside Den
  • Expression indexesden:"index", den:"unique", nullable unique for pointer fields

Quick Start

mkdir myapp && cd myapp
go mod init myapp
go get github.com/oliverandrich/den@latest
package main

import (
    "context"
    "fmt"
    "log"

    "github.com/oliverandrich/den"
    _ "github.com/oliverandrich/den/backend/sqlite" // register sqlite:// scheme
    "github.com/oliverandrich/den/document"
    "github.com/oliverandrich/den/where"
)

type Product struct {
    document.Base
    Name  string  `json:"name"  den:"index"`
    Price float64 `json:"price" den:"index"`
}

func main() {
    ctx := context.Background()

    // Open a SQLite database
    db, err := den.OpenURL(ctx, "sqlite:///products.db")
    if err != nil {
        log.Fatal(err)
    }
    defer db.Close()

    // Register document types (creates tables and indexes)
    if err := den.Register(ctx, db, &Product{}); err != nil {
        log.Fatal(err)
    }

    // Save — empty ID → insert, non-empty ID → update. Same call covers both.
    p := &Product{Name: "Widget", Price: 9.99}
    if err := den.Save(ctx, db, p); err != nil {
        log.Fatal(err)
    }
    fmt.Printf("Saved: %s (ID: %s)\n", p.Name, p.ID)

    // Query
    products, err := den.NewQuery[Product](db,
        where.Field("price").Lt(20.0),
    ).Sort("name", den.Asc).All(ctx)
    if err != nil {
        log.Fatal(err)
    }
    for _, prod := range products {
        fmt.Printf("  %s — $%.2f\n", prod.Name, prod.Price)
    }

    // Iterate (streaming, memory-efficient)
    for doc, err := range den.NewQuery[Product](db).Iter(ctx) {
        if err != nil {
            log.Fatal(err)
        }
        fmt.Printf("  %s\n", doc.Name)
    }
}

To use PostgreSQL instead, change the DSN and the import:

import _ "github.com/oliverandrich/den/backend/postgres" // instead of sqlite

db, err := den.OpenURL(ctx, "postgres://user:pass@localhost/mydb")

Architecture

den/
├── den.go, crud.go, query.go       Public API: Open, Save, FindByID, NewQuery, …
├── aliases.go, options.go          Type aliases + CRUDOption / LockOption constructors
├── errors.go                       Error sentinels (re-exports)
├── engine/                         Engine — every implementation file, plus tests
├── internal/util/                  Shared helpers (reflect, sql safety, field validation)
├── storage/                        Storage backend registry + OpenURL
├── storage/file/                   Local filesystem backend (file:// scheme)
├── document/                       Base + composable SoftDelete, Tracked, Attachment embeds
├── where/                          Query condition builders
├── backend/
│   ├── sqlite/                     SQLite backend (pure Go, no CGO)
│   └── postgres/                   PostgreSQL backend (pgx)
├── validate/                       Struct tag validation entry point
├── migrate/                        Migration framework
└── dentest/                        Test helpers

The root package is a thin convenience surface — six files of type aliases and one-line wrapper functions. Everything load-bearing lives in engine/, a public package that applications can import directly when they need access to types the root doesn't surface. Aliases preserve identity (den.QuerySet[T] IS engine.QuerySet[T]), so the indirection is free at runtime.

Backend Interface

Both backends implement the same Backend interface. The ReadWriter subset is shared between backends and transactions, so CRUD code works identically inside and outside transactions.

type ReadWriter interface {
    Get(ctx, collection, id) ([]byte, error)
    Put(ctx, collection, id, data) error
    Delete(ctx, collection, id) error
    Query(ctx, collection, *Query) (Iterator, error)
    Count(ctx, collection, *Query) (int64, error)
    Exists(ctx, collection, *Query) (bool, error)
    Aggregate(ctx, collection, op, field, *Query) (*float64, error)
}
Document Types

Every document embeds document.Base — the required anchor that provides ID, CreatedAt, UpdatedAt, Rev. Opt-in features are available as separate composable embeds:

Embed Purpose
document.Base Required. Provides ID, CreatedAt, UpdatedAt, Rev
document.SoftDelete Adds DeletedAt and IsDeleted() for non-destructive deletion
document.Tracked Adds byte-snapshot machinery for IsChanged, GetChanges, Revert
document.Attachment Adds StoragePath, Mime, Size, SHA256 — file reference paired with a den.Storage backend

Compose freely: struct { document.Base; document.SoftDelete; document.Tracked; document.Attachment; ... }.

Query Operators
where.Field("price").Gt(10)           // comparison
where.Field("status").In("a", "b")    // set membership
where.Field("tags").Contains("go")    // array contains
where.Field("email").IsNil()          // null check
where.Field("name").RegExp("^W")      // regular expression
where.And(cond1, cond2)               // logical combinators
where.Field("addr.city").Eq("Berlin") // nested fields (dot notation)

Validation

Den runs validate struct-tag constraints automatically on every Save — there is no opt-in and no way to bypass them from inside Den. Add tags via go-playground/validator:

type User struct {
    document.Base
    Name  string `json:"name"  den:"unique" validate:"required,min=3,max=50"`
    Email string `json:"email" den:"unique" validate:"required,email"`
    Age   int    `json:"age"                validate:"gte=0,lte=130"`
}

Errors wrap den.ErrValidation and can be inspected for field-level detail:

err := den.Save(ctx, db, &User{Name: "ab"})
if errors.Is(err, den.ErrValidation) {
    var ve *validate.Errors
    if errors.As(err, &ve) {
        for _, fe := range ve.Fields {
            fmt.Printf("%s failed on %s\n", fe.Field, fe.Tag)
        }
    }
}

Tag validation and the Validator interface coexist — tag validation runs first (structural rules), then Validate() (business logic). The validate/ package also exports validate.Document(doc) for callers that want to run the same checks outside the Den boundary (HTTP handlers, form parsers). For validating arbitrary non-document structs, use go-playground/validator/v10 directly.

Testing

Den provides a dentest package for test setup:

func TestMyFeature(t *testing.T) {
    db := dentest.MustOpen(t, &Product{}, &Category{})
    // File-backed SQLite in t.TempDir(), auto-closed via t.Cleanup
}

For PostgreSQL tests:

func TestMyFeature(t *testing.T) {
    db := dentest.MustOpenPostgres(t, "postgres://localhost/test", &Product{})
}

Benchmarks

Measured on an Apple M4 Pro (14 cores), Go 1.25, PostgreSQL 17 on localhost. The fixture is a ~1 KB article document (title, body, status, category, tags, price, indexed timestamp, embedded author link, metadata map) — closer to a real blog or catalog entry than a minimal struct.

Reproduce locally with mise run bench-readme. Numbers exclude connection-setup overhead (the bench helper opens the DB once and reuses it).

Serial workloads

Single-goroutine latency per operation. Lower is better.

Scenario SQLite Postgres SQLite allocs Postgres allocs
Save (insert) 150.1 µs 159.0 µs 49 47
SaveAll (100) 9.95 ms 13.58 ms 5211 4716
SaveAll (1000) 91.94 ms 139.92 ms 52026 47071
FindByID 104.0 µs 424.4 µs 62 59
FindByIDs (10) 264.2 µs 947.7 µs 343 329
Query + Sort + Limit(10) 723.8 µs 1.84 ms 327 291
Query + Sort + Limit(100) 1.90 ms 4.69 ms 2939 2544
Iter (1000 rows) 2.65 ms 2.78 ms 29044 25029
Count(filter) 25.3 µs 780.7 µs 29 31
Sum(filter) 177.2 µs 1.04 ms 35 41
FTS Search 902.6 µs 2.13 ms 604 513
WithFetchLinks (20 rows) 74.0 µs 438.5 µs 656 570
Save (update) 140.3 µs 349.8 µs 100 96
QuerySet.Update (100) 9.28 ms 21.22 ms 7049 6143
RunInTransaction 181.0 µs 330.1 µs 116 102
Concurrent workloads

b.RunParallel with Go's default GOMAXPROCS. Higher ops/sec is better. SQLite serializes writers by design (BEGIN IMMEDIATE), so write-heavy numbers plateau at single-writer speed; PostgreSQL's MVCC scales writes across connections.

Scenario SQLite Postgres
FindByID 8.6k ops/s 3.1k ops/s
Save (insert) 4.7k ops/s 30.3k ops/s
Mixed reads/writes 80/20 14.8k ops/s 3.7k ops/s
Queue consumer (SkipLocked) 24.7k ops/s 21.9k ops/s

Development

Den uses mise for tool pinning and task running. .mise.toml pins the Go toolchain plus tparse, golangci-lint, goimports, govulncheck, go-licenses, and pre-commit:

mise install                # Install pinned tools from .mise.toml
mise run setup              # Verify dev environment (also installs tools)
mise run test               # Run all tests (SQLite + PostgreSQL)
mise run lint               # Run golangci-lint
mise run fmt                # Format all Go files
mise run coverage           # Run tests with coverage report
mise run coverage-check     # Enforce per-package coverage threshold
mise run vuln               # Run vulnerability check
mise run tidy               # Tidy module dependencies
mise run beans              # List active beans (issue tracker)

Requires Go 1.25+ (managed by mise). Run mise run setup to verify your dev environment.

Dependencies

Dependency Purpose
modernc.org/sqlite SQLite backend (pure Go, no CGO)
github.com/jackc/pgx/v5 PostgreSQL backend
github.com/go-playground/validator/v10 Struct tag validation (optional, via den/validate)

License

Den is licensed under the MIT License.

The Go Gopher was originally designed by Renee French and is licensed under CC BY 4.0.

Documentation

Overview

Package den is an ODM for Go with two storage backends (SQLite and PostgreSQL). It exposes a single chainable QuerySet for filter-and-act operations and a small set of doc-in-hand top-level functions for persistence (Save, Delete, FindByID, Refresh).

This root package is the convenience surface: type aliases for every exported type plus thin wrapper functions for every exported function. The implementation lives in github.com/oliverandrich/den/engine, which is a public package — applications that need types or functions the root does not re-export (e.g. when building tooling) can import it directly. The aliases preserve type identity, so `den.QuerySet[T]` IS `engine.QuerySet[T]`.

Index

Constants

View Source
const (
	// SortDirection
	Asc  = engine.Asc
	Desc = engine.Desc

	// AggregateOp
	OpSum   = engine.OpSum
	OpAvg   = engine.OpAvg
	OpMin   = engine.OpMin
	OpMax   = engine.OpMax
	OpCount = engine.OpCount

	// LinkRule
	LinkIgnore = engine.LinkIgnore
	LinkWrite  = engine.LinkWrite
	LinkDelete = engine.LinkDelete

	// LockMode
	LockDefault    = engine.LockDefault
	LockSkipLocked = engine.LockSkipLocked
	LockNoWait     = engine.LockNoWait

	// Reserved field name constants
	FieldID           = engine.FieldID
	FieldCreatedAt    = engine.FieldCreatedAt
	FieldUpdatedAt    = engine.FieldUpdatedAt
	FieldRev          = engine.FieldRev
	FieldDeletedAt    = engine.FieldDeletedAt
	FieldDeletedBy    = engine.FieldDeletedBy
	FieldDeleteReason = engine.FieldDeleteReason
)

Variables

View Source
var (
	ErrNotFound                = engine.ErrNotFound
	ErrMultipleMatches         = engine.ErrMultipleMatches
	ErrDuplicate               = engine.ErrDuplicate
	ErrRevisionConflict        = engine.ErrRevisionConflict
	ErrNotRegistered           = engine.ErrNotRegistered
	ErrValidation              = engine.ErrValidation
	ErrTransactionFailed       = engine.ErrTransactionFailed
	ErrNoSnapshot              = engine.ErrNoSnapshot
	ErrMigrationFailed         = engine.ErrMigrationFailed
	ErrLocked                  = engine.ErrLocked
	ErrDeadlock                = engine.ErrDeadlock
	ErrSerialization           = engine.ErrSerialization
	ErrFTSNotSupported         = engine.ErrFTSNotSupported
	ErrLockRequiresTransaction = engine.ErrLockRequiresTransaction
	ErrIncompatiblePagination  = engine.ErrIncompatiblePagination
	ErrUnsupportedScheme       = engine.ErrUnsupportedScheme
)

Error sentinels re-exported from den/engine so callers keep matching via `errors.Is(err, den.ErrNotFound)`.

Functions

func AdvisoryLock added in v0.8.0

func AdvisoryLock(ctx context.Context, tx *Tx, key int64) error

AdvisoryLock acquires a transaction-scoped advisory lock identified by key. PostgreSQL only; SQLite no-ops.

func Collections

func Collections(db *DB) []string

Collections returns the names of every registered collection.

func Delete

func Delete[T any](ctx context.Context, s Scope, document *T, opts ...CRUDOption) error

Delete removes a document. Soft-deletes when the document embeds `document.SoftDelete`; pass HardDelete() to bypass.

func DeleteAll added in v0.12.0

func DeleteAll[T any](ctx context.Context, s Scope, docs []*T, opts ...CRUDOption) error

DeleteAll deletes every doc in docs in a single transaction. Fail-fast.

func FetchAllLinks[T any](ctx context.Context, s Scope, doc *T) error

FetchAllLinks resolves the direct link fields on doc — single-level, the loaded targets' own links stay untouched.

func FetchLink[T any](ctx context.Context, s Scope, doc *T, fieldName string) error

FetchLink resolves a single named link field on a document.

func FetchLinkField added in v0.11.0

func FetchLinkField[T any](ctx context.Context, s Scope, link *Link[T]) error

FetchLinkField resolves the link by typed pointer instead of a stringly-named field on the parent.

func FindByID

func FindByID[T any](ctx context.Context, s Scope, id string, opts ...CRUDOption) (*T, error)

FindByID retrieves a document by its ID. Returns ErrNotFound if no row matches. Explicit-by-ID lookups bypass the soft-delete filter — callers can check Value.IsDeleted() to react.

func FindByIDs

func FindByIDs[T any](ctx context.Context, s Scope, ids []string, opts ...CRUDOption) ([]*T, error)

FindByIDs retrieves multiple documents by their IDs in a single query. Missing IDs are silently skipped.

func GetChanges

func GetChanges[T any](db *DB, doc *T) (map[string]FieldChange, error)

GetChanges returns a map of field names to their before/after values for all fields that changed since the document was loaded.

func IsChanged

func IsChanged[T any](db *DB, doc *T) (bool, error)

IsChanged reports whether the document has changed since it was loaded. Returns false if the document has no snapshot (never loaded or not Trackable).

func LiteralFTS5 added in v0.17.0

func LiteralFTS5(term string) string

LiteralFTS5 builds a literal-terms FTS5 MATCH string from a raw term: whitespace-separated tokens become double-quoted literals ANDed together, neutralising FTS5 operators and punctuation. QuerySet.Search applies this automatically, so most callers never need it directly — reach for it only to compose a safe literal core with QuerySet.SearchRaw.

func LockByID added in v0.8.0

func LockByID[T any](ctx context.Context, tx *Tx, id string, opts ...LockOption) (*T, error)

LockByID loads a document with a row-level lock held until the surrounding transaction commits or rolls back. Must be called inside a transaction; ErrLockRequiresTransaction otherwise.

func Marshal added in v0.17.0

func Marshal(v any) ([]byte, error)

Marshal is Den's output JSON marshaller: like json.Marshal, but any hydrated (Loaded) Link[T] anywhere in the value graph is emitted as its resolved object instead of the bare id. Unloaded links and all other fields marshal identically to the standard library — the default wire format (and Den's storage encoding) is unchanged. Hydrate the links you want expanded with QuerySet.WithFetchLinks, then Marshal for the response.

func NewID added in v0.4.0

func NewID() string

NewID generates a new ULID string. ULIDs are lexicographically sortable and timestamp-ordered. Use this for document IDs, worker IDs, or any unique identifier.

func PreserveServerFields added in v0.17.0

func PreserveServerFields[T any](db *DB, dst, src *T) error

PreserveServerFields copies Den's server-owned fields (_id, _created_at, _updated_at, _rev, and the soft-delete audit fields) from src onto dst, leaving client-owned fields untouched. The building block behind Replace, for callers that load and persist the existing record themselves.

func Refresh

func Refresh[T any](ctx context.Context, s Scope, document *T, opts ...CRUDOption) error

Refresh re-reads a document from the database by its ID, overwriting all fields on the provided struct.

func RefreshAll added in v0.12.0

func RefreshAll[T any](ctx context.Context, s Scope, docs []*T, opts ...CRUDOption) error

RefreshAll re-reads every doc in docs in a single transaction.

func Register

func Register(ctx context.Context, db *DB, types ...document.Document) error

Register registers one or more document types with the database. It must be called before any CRUD or query operation on the types. Types must embed document.Base to satisfy document.Document — the sealed marker enforces this at compile time.

func RegisterBackend added in v0.2.0

func RegisterBackend(scheme string, opener func(ctx context.Context, dsn string) (Backend, error))

RegisterBackend registers a backend opener under the given URL scheme. Called from backend packages' init() functions; not typically called by application code.

func Replace added in v0.17.0

func Replace[T any](ctx context.Context, s Scope, fresh *T, opts ...CRUDOption) error

Replace performs a full-content replace (PUT semantics): fresh's client-owned fields overwrite the stored row — omitted fields reset to zero — while Den's server-owned identity, audit, and soft-delete fields are preserved from the existing record. Last-writer-wins (adopts the stored revision); does not resurrect soft-deleted documents. Returns ErrNotFound if no row matches fresh's ID, ErrValidation if it has none.

func Revert added in v0.8.0

func Revert[T any](db *DB, doc *T) error

Revert restores the document to its state at load time by decoding the stored snapshot back over its fields. Returns ErrNoSnapshot if the document was never loaded or does not embed `document.Tracked`.

func RunInTransaction

func RunInTransaction(ctx context.Context, db *DB, fn func(tx *Tx) error) error

RunInTransaction opens a write transaction on db, runs fn inside it, and commits on success. Any non-nil error from fn rolls back the transaction; the same error is returned to the caller.

func Save added in v0.11.0

func Save[T any](ctx context.Context, s Scope, document *T, opts ...CRUDOption) error

Save inserts the document if its ID is empty, otherwise updates it. The single doc-in-hand persistence entry point.

func SaveAll added in v0.12.0

func SaveAll[T any](ctx context.Context, s Scope, docs []*T, opts ...CRUDOption) error

SaveAll persists every doc in docs in a single transaction. Fail-fast: any per-doc error rolls back the batch.

Types

type AfterDeleter

type AfterDeleter = engine.AfterDeleter

type AfterInserter

type AfterInserter = engine.AfterInserter

type AfterSaver

type AfterSaver = engine.AfterSaver

type AfterSoftDeleter added in v0.11.0

type AfterSoftDeleter = engine.AfterSoftDeleter

type AfterUpdater

type AfterUpdater = engine.AfterUpdater

type AggregateOp

type AggregateOp = engine.AggregateOp

type Backend

type Backend = engine.Backend

type BeforeDeleter

type BeforeDeleter = engine.BeforeDeleter

type BeforeInserter

type BeforeInserter = engine.BeforeInserter

type BeforeSaver

type BeforeSaver = engine.BeforeSaver

type BeforeSoftDeleter added in v0.11.0

type BeforeSoftDeleter = engine.BeforeSoftDeleter

type BeforeUpdater

type BeforeUpdater = engine.BeforeUpdater

type CRUDOption

type CRUDOption = engine.CRUDOption

func HardDelete

func HardDelete() CRUDOption

HardDelete permanently removes a soft-deleteable document instead of flipping its DeletedAt.

func IgnoreRevision

func IgnoreRevision() CRUDOption

IgnoreRevision skips the optimistic-concurrency revision check on Save's update path. Use sparingly — race losers get silently overwritten.

func IncludeDeleted added in v0.11.0

func IncludeDeleted() CRUDOption

IncludeDeleted makes by-ID lookups (FindByID, FindByIDs) consider soft-deleted documents. Mirrors QuerySet.IncludeDeleted().

func SoftDeleteBy added in v0.11.0

func SoftDeleteBy(actor string) CRUDOption

SoftDeleteBy records an actor on the document's DeletedBy field on the soft-delete path. No-op on HardDelete() and on types that don't embed `document.SoftDelete`.

func SoftDeleteReason added in v0.11.0

func SoftDeleteReason(reason string) CRUDOption

SoftDeleteReason records a free-form reason on the document's DeleteReason field on the soft-delete path.

func WithLinkRule

func WithLinkRule(rule LinkRule) CRUDOption

WithLinkRule sets the link cascading rule for Save / Delete and the QuerySet write terminals.

func WithoutFetchLinks() CRUDOption

WithoutFetchLinks suppresses link hydration on a doc-in-hand read, including fields tagged `den:"eager"`. Honored by FindByID, FindByIDs, and Refresh.

type CollectionMeta

type CollectionMeta = engine.CollectionMeta

func Meta

func Meta[T any](db *DB) (CollectionMeta, error)

Meta returns the registered metadata for type T. Returns ErrNotRegistered if T has not been registered.

type DB

type DB = engine.DB

func Open

func Open(ctx context.Context, backend Backend, opts ...Option) (*DB, error)

Open creates a new DB using the given backend directly. The context governs any registration work triggered by WithTypes (collection table creation, index provisioning); callers with long-running startup work can pass a timeout or cancellable context to abort it cleanly.

Use OpenURL for URL-based opening with automatic backend selection.

func OpenURL added in v0.2.0

func OpenURL(ctx context.Context, dsn string, opts ...Option) (*DB, error)

OpenURL opens a database by DSN, dispatching to the registered backend for the scheme (e.g. sqlite:///path.db, postgres://...). The backend must be registered first via a side-effect import of its package.

type DanglingLinkError added in v0.11.0

type DanglingLinkError = engine.DanglingLinkError

type DenSettable

type DenSettable = engine.DenSettable

type DropStaleOption added in v0.8.0

type DropStaleOption = engine.DropStaleOption

func DryRun added in v0.8.0

func DryRun() DropStaleOption

DryRun makes DropStaleIndexes report what it would drop without touching the schema.

type DropStaleResult added in v0.8.0

type DropStaleResult = engine.DropStaleResult

func DropStaleIndexes added in v0.8.0

func DropStaleIndexes(ctx context.Context, db *DB, opts ...DropStaleOption) (DropStaleResult, error)

DropStaleIndexes removes indexes that exist on the backend but are no longer declared by any registered type. Pass DryRun() to report what would change without touching the schema.

type FTSProvider

type FTSProvider = engine.FTSProvider

type FTSSearcher added in v0.11.0

type FTSSearcher = engine.FTSSearcher

type FieldChange

type FieldChange = engine.FieldChange

type FieldMeta

type FieldMeta = engine.FieldMeta

type GroupByAgg added in v0.7.0

type GroupByAgg = engine.GroupByAgg

type GroupByBuilder

type GroupByBuilder[T any] = engine.GroupByBuilder[T]

type GroupByRow added in v0.7.0

type GroupByRow = engine.GroupByRow

type GroupBySortEntry added in v0.11.0

type GroupBySortEntry = engine.GroupBySortEntry

type IndexDefinition

type IndexDefinition = engine.IndexDefinition

type Iterator

type Iterator = engine.Iterator
type Link[T any] = engine.Link[T]
func NewLink[T any](doc *T) Link[T]

NewLink creates a Link from an existing document, extracting its ID from the embedded `document.Base`.

type LinkFieldMeta added in v0.17.0

type LinkFieldMeta = engine.LinkFieldMeta

func LinkFields added in v0.17.0

func LinkFields[T any](db *DB) ([]LinkFieldMeta, error)

LinkFields enumerates the Link[T] / []Link[T] relation fields of the registered document type T (JSON name, target collection, single-vs-slice, eager). Returns ErrNotRegistered if T is not registered. Use it to validate or allowlist expandable relations without re-implementing reflection.

type LinkRule

type LinkRule = engine.LinkRule

type LockMode added in v0.8.0

type LockMode = engine.LockMode

type LockOption added in v0.8.0

type LockOption = engine.LockOption

func NoWait added in v0.8.0

func NoWait() LockOption

NoWait makes LockByID return ErrLocked immediately when the row is already locked, instead of blocking. PostgreSQL only.

func SkipLocked added in v0.8.0

func SkipLocked() LockOption

SkipLocked makes LockByID return ErrNotFound when the row is already locked, instead of blocking. PostgreSQL only.

type Option

type Option = engine.Option

func WithStorage added in v0.9.0

func WithStorage(s Storage) Option

WithStorage attaches an attachment Storage to the DB. Required when any registered type carries `document.Attachment` fields.

func WithTypes added in v0.8.0

func WithTypes(types ...document.Document) Option

WithTypes queues document types to be registered at the end of Open. Equivalent to calling Register(ctx, db, types...) after Open returns. Types must embed document.Base to satisfy document.Document — the sealed marker enforces this at compile time.

type Query

type Query = engine.Query

type QuerySet

type QuerySet[T any] = engine.QuerySet[T]

func NewQuery

func NewQuery[T any](scope Scope, conditions ...where.Condition) QuerySet[T]

NewQuery creates a new chainable query for type T. Scope is `*DB` (outside a transaction) or `*Tx` (inside one). The context is supplied later by the terminal method, so one `QuerySet` can be reused across contexts.

type ReadWriter

type ReadWriter = engine.ReadWriter

type RecordedIndex added in v0.8.0

type RecordedIndex = engine.RecordedIndex

type Scope added in v0.8.0

type Scope = engine.Scope

type SeekableStorage added in v0.11.2

type SeekableStorage = engine.SeekableStorage

type SetFields

type SetFields = engine.SetFields

type Settings

type Settings = engine.Settings

type SortDirection

type SortDirection = engine.SortDirection

type SortEntry

type SortEntry = engine.SortEntry

type StaleIndex added in v0.8.0

type StaleIndex = engine.StaleIndex

type Storage added in v0.9.0

type Storage = engine.Storage

type Transaction

type Transaction = engine.Transaction

type Tx

type Tx = engine.Tx

type Validator

type Validator = engine.Validator

Directories

Path Synopsis
Package backend defines the contract every den storage engine implements: the Backend interface, plus the supporting types passed across it (Query, LockMode, AggregateOp, etc.).
Package backend defines the contract every den storage engine implements: the Backend interface, plus the supporting types passed across it (Query, LockMode, AggregateOp, etc.).
postgres
Package postgres implements the Den backend for PostgreSQL via jackc/pgx/v5.
Package postgres implements the Den backend for PostgreSQL via jackc/pgx/v5.
sqlite
Package sqlite implements the Den backend for SQLite via modernc.org/sqlite (pure Go, no CGO).
Package sqlite implements the Den backend for SQLite via modernc.org/sqlite (pure Go, no CGO).
Package dentest provides test helpers for opening a Den database in a temporary directory (SQLite) or against a reachable PostgreSQL instance.
Package dentest provides test helpers for opening a Den database in a temporary directory (SQLite) or against a reachable PostgreSQL instance.
Package engine is the den ODM implementation: DB and Tx, the chainable QuerySet, CRUD entry points, link hydration, lifecycle hooks, revision tracking, and soft-delete handling.
Package engine is the den ODM implementation: DB and Tx, the chainable QuerySet, CRUD entry points, link hydration, lifecycle hooks, revision tracking, and soft-delete handling.
Package idgen produces ULID-format document IDs for Den.
Package idgen produces ULID-format document IDs for Den.
internal
cmd/bench-report command
Command bench-report reads `go test -bench` output on stdin and splices the benchmark results into README.md between the sentinel markers
Command bench-report reads `go test -bench` output on stdin and splices the benchmark results into README.md between the sentinel markers
Package lock defines the option vocabulary for row-level and advisory locks used by engine.LockByID, engine.AdvisoryLock, and QuerySet.ForUpdate.
Package lock defines the option vocabulary for row-level and advisory locks used by engine.LockByID, engine.AdvisoryLock, and QuerySet.ForUpdate.
Package maintenance defines the data shapes and option vocabulary for den's admin-side operations — notably engine.DropStaleIndexes, the post-schema-change cleanup that drops indexes Den previously created but no longer recognises.
Package maintenance defines the data shapes and option vocabulary for den's admin-side operations — notably engine.DropStaleIndexes, the post-schema-change cleanup that drops indexes Den previously created but no longer recognises.
Package search defines the full-text-search contract that backends (and, where supported, their transactions) implement.
Package search defines the full-text-search contract that backends (and, where supported, their transactions) implement.
Package storage defines the Storage interface that backs document.Attachment fields plus the registry OpenURL uses to construct a Storage from a URL-style DSN.
Package storage defines the Storage interface that backs document.Attachment fields plus the registry OpenURL uses to construct a Storage from a URL-style DSN.
file
Package file provides a local-filesystem Storage backend for Den.
Package file provides a local-filesystem Storage backend for Den.
Package validate exposes the struct-tag constraint helper used by Den.
Package validate exposes the struct-tag constraint helper used by Den.

Jump to

Keyboard shortcuts

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