Help us shape the future of Go! Take the 2026 Go Developer Survey

storage

package
v0.0.0-...-d5346c1 Latest Latest
Warning

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

Go to latest
Published: Oct 30, 2026 License: MIT Imports: 5 Imported by: 0

Documentation

Overview

Package storage persists FHIR resources and the indexes search runs against.

The Backend interface is the only thing the rest of the server talks to, and no SQL appears outside its implementations. That boundary is deliberate: a second backend retrofitted onto a query layer that has quietly grown one engine's habits is exactly how a "portable" abstraction turns out not to be. Keeping the query surface expressed as values rather than strings is what made the PostgreSQL port a translation rather than a rewrite.

There is one SQL implementation, in internal/storage/sqlstore, and the engines supply only what they genuinely do differently. What keeps them honest is internal/storage/storagetest: the identical suite runs against both, so an assertion that cannot hold on one is a documented divergence or a bug rather than a surprise in production.

Index

Constants

This section is empty.

Variables

View Source
var (
	// ErrNotFound: no such resource. 404.
	ErrNotFound = errors.New("storage: resource not found")
	// ErrDeleted: the resource existed and was deleted. 410 Gone, which is
	// distinct from 404 and observable to clients.
	ErrDeleted = errors.New("storage: resource deleted")
	// ErrConflict: an If-Match precondition did not hold. 409.
	ErrConflict = errors.New("storage: version conflict")
	// ErrDuplicate: a create would collide with an existing id. 409.
	ErrDuplicate = errors.New("storage: resource already exists")
	// ErrMultipleMatches: a conditional operation matched more than one
	// resource, which the specification makes an error rather than a choice.
	ErrMultipleMatches = errors.New("storage: conditional operation matched multiple resources")
)

Errors the REST layer maps onto status codes.

Functions

This section is empty.

Types

type Backend

type Backend interface {
	// Create stores a new resource, assigning version 1. The node's id must
	// already be set. It returns ErrDuplicate if the id is taken.
	Create(ctx context.Context, node *resource.Node) (*Resource, error)

	// Update stores a new version, creating the resource if absent (which FHIR
	// permits: a PUT to an unused id is a create). ifMatch, when non-empty, is
	// the version the caller believes is current; a mismatch is ErrConflict.
	Update(ctx context.Context, node *resource.Node, ifMatch string) (created bool, res *Resource, err error)

	// Read returns the current version, or ErrDeleted if it is a tombstone.
	Read(ctx context.Context, resourceType, id string) (*Resource, error)

	// VRead returns one specific version, tombstones included.
	VRead(ctx context.Context, resourceType, id, versionID string) (*Resource, error)

	// Delete tombstones a resource. Deleting an absent resource succeeds and
	// reports existed=false, which the specification requires so that delete is
	// idempotent.
	Delete(ctx context.Context, resourceType, id, ifMatch string) (existed bool, res *Resource, err error)

	// History returns versions newest first.
	History(ctx context.Context, q HistoryQuery) ([]*Resource, error)

	// Search returns one page of the current versions matching a query.
	Search(ctx context.Context, q SearchQuery) (SearchResult, error)

	// Tx runs fn against a backend whose writes commit together: every Create,
	// Update, and Delete fn performs either lands or none of them do.
	// Returning an error rolls all of them back.
	//
	// It exists for transaction bundles, where atomicity is the whole point --
	// a half-applied transaction leaves a client with no way to know what
	// happened. Reads inside fn see fn's own uncommitted writes, which is what
	// lets a later entry reference a resource an earlier one created. Nested
	// calls join the enclosing transaction rather than starting a new one.
	Tx(ctx context.Context, fn func(context.Context, Backend) error) error

	// Include resolves _include and _revinclude against a set of matches,
	// returning the additional resources to place in the bundle. Resources
	// already among the seeds are not returned again.
	Include(ctx context.Context, seeds []*Resource, specs []IncludeSpec) ([]*Resource, error)

	// Close releases the backend's resources.
	Close() error
}

Backend is the persistence contract.

Every method takes a context so a slow query cannot outlive its request.

type Chain

type Chain struct {
	// TargetType restricts which referenced type is followed, from the
	// "subject:Patient" form. Empty follows any.
	TargetType string
	Params     []ParamMatch
}

Chain is a forward reference join, from "subject.name=peter" or "subject:Patient.name=peter". Chains nest, so Params may contain further chained matches.

type CompositeMatch

type CompositeMatch struct {
	Components []ParamMatch
}

CompositeMatch is one alternative of a composite parameter: the component matches that a single occurrence of the composite's base expression has to satisfy together.

type FilterExpr

type FilterExpr struct {
	// Op is FilterAnd, FilterOr, or FilterNot; empty for a leaf.
	Op       FilterOp
	Operands []*FilterExpr
	Match    *ParamMatch
}

FilterExpr is a boolean combination of parameter matches, from _filter.

A node is either a leaf holding one Match, or an operator with Operands. "not" takes exactly one operand.

type FilterOp

type FilterOp string

FilterOp is a boolean connective in a _filter expression.

const (
	FilterAnd FilterOp = "and"
	FilterOr  FilterOp = "or"
	FilterNot FilterOp = "not"
)

type Has

type Has struct {
	// SourceType is the referencing resource type.
	SourceType string
	// Code is the reference parameter on the source that points back here.
	Code   string
	Params []ParamMatch
}

Has is a reverse reference join, from "_has:Observation:subject:code=1234": find resources that some Observation points at through its subject parameter, where that Observation matches the remaining criteria.

type HistoryQuery

type HistoryQuery struct {
	// Type and ID narrow the scope: both empty is system-wide history, Type
	// alone is type-wide, both set is one resource's history.
	Type string
	ID   string
	// Since restricts to versions updated at or after this instant.
	Since time.Time
	// Count is the maximum number of entries; zero means the backend default.
	Count int
	// Offset skips entries, for paging.
	Offset int
}

HistoryQuery selects a slice of the version history.

type IncludeSpec

type IncludeSpec struct {
	// Reverse distinguishes _revinclude (find resources pointing at the
	// matches) from _include (find resources the matches point at).
	Reverse bool
	// SourceType is the resource type holding the reference. For _include it is
	// the type being searched; for _revinclude it is the type doing the
	// referencing.
	SourceType string
	// Code is the reference search parameter to follow. Empty with Wildcard
	// set means every reference.
	Code     string
	Wildcard bool
	// TargetType optionally restricts which referenced type is pulled in.
	TargetType string
	// Iterate repeats the expansion over what it finds, for :iterate.
	Iterate bool
}

IncludeSpec is one _include or _revinclude request.

type MatchValue

type MatchValue struct {
	// System and Code together match a token; Code alone matches any system.
	System string
	Code   string
	// Text matches a string index; Match says how. A prefix or contains match
	// is compared on the folded form, an exact one on the value as written.
	Text  string
	Match StringMatch
	// Reference matches a reference index by target type and id, or by the
	// reference URL when the query gives an absolute one.
	RefType string
	RefID   string
	RefURL  string
	// DateLow and DateHigh bound a date range in microseconds since the epoch.
	DateLow, DateHigh int64
	// NumLow and NumHigh bound a number or quantity range.
	NumLow, NumHigh float64
	// QuantitySystem and QuantityCode narrow a quantity to a unit.
	QuantitySystem, QuantityCode string
	// URI matches a uri index exactly.
	URI string
	// Prefix is the comparison the value carries: eq, ne, gt, lt, ge, le, sa,
	// eb, or ap. Empty means eq.
	Prefix string
	// Prefix scoping for uri parameters: Above matches the stored value's
	// ancestors, Below its descendants.
	URIAbove bool
	URIBelow bool

	// Missing, when set, matches resources that have (or lack) the parameter
	// rather than any particular value.
	Missing *bool
}

MatchValue is one alternative within a ParamMatch.

type ParamMatch

type ParamMatch struct {
	// Code is the search parameter's name, as it appears in a query string.
	Code string
	// Kind is the index the match runs against.
	Kind index.Kind
	// Values are alternatives: a match succeeds if any of them does, which is
	// how FHIR's comma-separated "or" works.
	Values []MatchValue
	// Negate inverts the whole match, for the :not modifier. It negates the
	// parameter rather than each value, which is the difference between "has no
	// value in this list" and "has some value not in this list" -- the
	// specification means the former.
	Negate bool
	// TextSearch redirects a token parameter to the string index, for the :text
	// modifier: extraction writes a CodeableConcept's text there under the same
	// parameter code.
	TextSearch bool

	// Chain turns this into a join through a reference: the resources this
	// parameter points at must themselves match Chain's parameters. It is how
	// "subject.name=peter" is expressed.
	Chain *Chain
	// Has is the reverse join: some resource must reference this one and match.
	// It is how "_has:Observation:subject:code=1234" is expressed.
	Has *Has
	// Composite holds the alternatives of a composite parameter, of which any
	// one satisfies the match. Within an alternative every component must be
	// satisfied by the *same* occurrence, which is what makes a composite
	// different from writing the components as separate parameters.
	Composite []CompositeMatch
}

ParamMatch is one indexed parameter constrained to a value.

type Resource

type Resource struct {
	Type string
	// ID is the logical id a client sees, which is an arbitrary string chosen
	// by the client or the server -- not the storage key.
	ID string
	// VersionID counts from 1 and increments on every write, including the
	// delete that tombstones a resource.
	VersionID   int64
	LastUpdated time.Time
	// Deleted marks a tombstone: the row records that a version existed and was
	// removed, which history and 410 responses both need.
	Deleted bool
	// Content is the canonical JSON of this version, with meta.versionId and
	// meta.lastUpdated already stamped. Empty for a tombstone.
	Content []byte
}

Resource is one stored version of a resource.

type SearchQuery

type SearchQuery struct {
	Type   string
	Params []ParamMatch
	Count  int
	Offset int
	SortBy []SortKey

	// Cursor resumes a previous page. When set, Offset is ignored: paging is by
	// keyset rather than by offset, so a resource created between two page
	// fetches cannot shift the ones after it.
	Cursor string

	// SkipTotal omits the count query, for _total=none. Counting is a second
	// full scan of the predicate, and a client paging through results rarely
	// needs it more than once.
	SkipTotal bool

	// Filter is the _filter expression, ANDed with Params. It is a tree rather
	// than a list because _filter is the one part of FHIR search that can say
	// "or" and "not" across whole parameters -- the query string itself can
	// only express a conjunction.
	Filter *FilterExpr
}

SearchQuery is a conjunction of parameter matches.

This is the query *plan*, not a query string: parsing the FHIR search syntax -- modifiers, prefixes, chaining -- happens above, and rendering to SQL happens inside a backend. Neither end knows about the other.

type SearchResult

type SearchResult struct {
	// Matches are the current versions on this page.
	Matches []*Resource
	// Total is the number of matches irrespective of paging, and is only
	// meaningful when HasTotal is set: a query with SkipTotal never counts,
	// and a zero there would be a number that means nothing.
	Total    int
	HasTotal bool
	// Next resumes the following page; empty on the last one.
	Next string
}

SearchResult is one page of matches.

type SortKey

type SortKey struct {
	Code       string
	Kind       index.Kind
	Descending bool
}

SortKey is one ordering term.

type StringMatch

type StringMatch int

StringMatch is how a string index value is compared.

const (
	// MatchPrefix is FHIR's default for string parameters: the stored value
	// starts with the query value, compared on the folded form.
	MatchPrefix StringMatch = iota
	// MatchExact compares the value as written, case and accents included.
	MatchExact
	// MatchContains looks anywhere in the value, for the :contains modifier.
	MatchContains
	// MatchEndsWith anchors at the end. No query-string modifier produces it;
	// it exists for _filter's "ew" operator.
	MatchEndsWith
)

Directories

Path Synopsis
Package index turns resources into search index entries.
Package index turns resources into search index entries.
Package postgres runs Nectar's storage on PostgreSQL.
Package postgres runs Nectar's storage on PostgreSQL.
Package sqlite runs Nectar's storage on SQLite.
Package sqlite runs Nectar's storage on SQLite.
Package sqlstore implements storage.Backend over SQL.
Package sqlstore implements storage.Backend over SQL.
Package storagetest is the storage conformance suite every backend must pass.
Package storagetest is the storage conformance suite every backend must pass.

Jump to

Keyboard shortcuts

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