Skip to content

Configuration

How to open a Den database, configure backends, and customize document behavior.


Opening a Database

OpenURL takes a context.Context as its first argument, a URL-style DSN as the second, and zero or more functional options as the third (variadic) argument — see the Options table below. Backend packages must be imported for side-effect registration.

import (
    "context"

    "github.com/oliverandrich/den"
    _ "github.com/oliverandrich/den/backend/sqlite"
    _ "github.com/oliverandrich/den/backend/postgres"
)

ctx := context.Background()

// SQLite: relative path (three slashes)
db, err := den.OpenURL(ctx, "sqlite:///data.db")

// SQLite: absolute path (four slashes)
db, err := den.OpenURL(ctx, "sqlite:////var/lib/myapp/data.db")

// SQLite: in-memory
db, err := den.OpenURL(ctx, "sqlite://:memory:")

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

// PostgreSQL (alias scheme)
db, err := den.OpenURL(ctx, "postgresql://user:pass@localhost/mydb")

Important: Backend packages register themselves via init(). You must import the backend package even if you don't reference it directly -- use the blank identifier _.

Options

OpenURL accepts functional options:

db, err := den.OpenURL(ctx, "sqlite:///data.db",
    den.WithTypes(&Product{}, &Order{}),
)
Option Package Description
den.WithTypes(types...) den Register the listed document types during Open instead of a separate Register call. Zero-value struct pointers, e.g. den.WithTypes(&Product{}, &Order{})
den.WithStorage(s) den Install a Storage for documents carrying document.Attachment fields

Struct-tag validation (validate:"...") runs automatically on every insert and update — no option required. See Validation.


DSN Formats

Backend Scheme Format Example
SQLite (relative) sqlite sqlite:///relative/path.db sqlite:///data/myapp.db
SQLite (absolute) sqlite sqlite:////absolute/path.db sqlite:////var/lib/myapp/data.db
SQLite (memory) sqlite sqlite://:memory: sqlite://:memory:
PostgreSQL postgres postgres://user:pass@host:port/dbname postgres://admin:secret@localhost:5432/myapp
PostgreSQL postgresql postgresql://user:pass@host:port/dbname postgresql://admin:secret@db.example.com/myapp

Registering Document Types

All document types must be registered before use. Registration creates the collection table and indexes.

err := den.Register(ctx, db,
    &Product{},
    &Category{},
    &User{},
)

Registration performs:

  1. Reflection-based analysis of the struct (fields, types, tags)
  2. Collection table creation (or verification if it already exists)
  3. Index creation based on den struct tags (additive -- new indexes are created, existing ones are kept)

DenSettings

Document types can customize their behavior by implementing the DenSettable interface:

type DenSettable interface {
    DenSettings() den.Settings
}

Settings Fields

Field Type Default Description
CollectionName string lowercase struct name Override the auto-derived collection name
UseRevision bool false Enable optimistic concurrency control via _rev field
Indexes []IndexDefinition nil Additional indexes (for compound indexes not expressible via struct tags)

Each IndexDefinition has Name string, Fields []string, and Unique bool — set Unique: true for compound unique indexes that struct tags can't express.

Example

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

func (p Product) DenSettings() den.Settings {
    return den.Settings{
        CollectionName: "products",       // override: default would be "product"
        UseRevision:    true,             // enable optimistic concurrency
        Indexes: []den.IndexDefinition{
            {
                Name:   "idx_category_price",
                Fields: []string{"category", "price"},  // compound index
            },
        },
    }
}

Collection Naming

By default, Den derives the collection name from the struct name:

  • Lowercase, no pluralization
  • Product becomes product
  • BlogPost becomes blogpost

Override via DenSettings().CollectionName:

func (p Product) DenSettings() den.Settings {
    return den.Settings{CollectionName: "products"}
}

Index Definition

Indexes can be defined in two ways:

Via Struct Tags

type Product struct {
    document.Base
    Name  string  `json:"name"  den:"index"`   // secondary index
    SKU   string  `json:"sku"   den:"unique"`  // unique index
    Body  string  `json:"body"  den:"fts"`     // full-text search index
}

Via Settings (compound indexes)

func (p Product) DenSettings() den.Settings {
    return den.Settings{
        Indexes: []den.IndexDefinition{
            {Name: "idx_category_price", Fields: []string{"category", "price"}},
        },
    }
}

Both approaches are additive. Struct tag indexes and Settings indexes are created together during registration.