Skip to content

Data model

Tuples

The smallest unit of data in XDB: an immutable path, attribute, and typed value.

core

A tuple is an attribute value at a record path: xdb://ns/schema/id#attr = value. It is the smallest unit of record data in XDB. A record groups tuples that share a path.

Structure

A tuple contains:

ComponentTypeDescription
Path*URIThe record URI (NS + Schema + ID)
AttrstringThe attribute name. Dots separate nested names
Value*ValueThe typed value
┌────────────────────────────────────────────────┐
│ Tuple │
├──────────┬──────────┬──────────────────────────┤
│ Path │ Attr │ Value │
│ (URI) │ (string) │ (typed: str, int, ...) │
└──────────┴──────────┴──────────────────────────┘

A tuple is immutable after creation. Its path, attribute, and value cannot change.

Creating Tuples

tuple := core.NewTuple("com.example/posts/post-123", "title", "Hello World")

NewTuple panics on invalid input. The path argument is a URI path without the xdb:// scheme. XDB infers the value type from the Go type. See Types for the supported types.

Usually you create tuples through a Record. The record owns the shared path:

record := core.NewRecord("com.example", "posts", "post-123").
Set("title", "Hello World")
tuple := record.Get("title")

Accessing Data

Path Components

tuple.Path() // *URI — record URI (without the attribute)
tuple.Attr() // string — attribute name
tuple.URI() // *URI — full URI, including the attribute fragment
tuple.Value() // *Value — typed value
tuple.Path().NS() // string — namespace
tuple.Path().Schema() // string — schema name
tuple.Path().ID() // string — record identifier

Typed Value Accessors

A tuple has As* methods (AsStr(), AsInt(), AsBool(), and more) that return the value with its type. Each method returns (T, error). If the attribute is missing, the tuple is nil, for example record.Get("tpyo"). The As* methods on a nil tuple return the zero value and ErrAttrNotFound. This makes a typo different from an empty value. See Types for the full list of accessors.

title, err := record.Get("title").AsStr()

Dot-Separated Attributes

An attribute name can contain dots to represent nested data, for example author.name or profile.address.city. The JSON encoder unfolds these attributes into nested objects. See Encoding for details.

Tuples in Stores

A store can read or write an individual attribute through its #attr URI:

st := store.New(xdbmemory.NewDriver())
// Patch tuples into records. The store creates the record if it is
// absent and leaves other attributes unchanged. Tuples can span records.
err := st.PutTuples(ctx,
core.NewTuple("com.example/posts/post-123", "title", "Hello"),
core.NewTuple("com.example/posts/post-123", "rating", 4.5),
)
// Point-read one tuple. If the record or the attribute is absent, the
// error is core.ErrNotFound.
tuple, err := st.GetTuple(ctx,
core.MustParseURI("xdb://com.example/posts/post-123#title"))
// Batch point reads omit absent attributes and return no error for them.
tuples, err := st.GetTuples(ctx, uris...)
// Delete single tuples. This is idempotent. When the last tuple of a
// record is deleted, the record is deleted.
err = st.DeleteTuples(ctx,
core.MustParseURI("xdb://com.example/posts/post-123#rating"))

A record starts to exist when its first tuples are written. It stops existing when its last tuple is deleted. Schema policy applies to tuple writes in the same way as to other writes. Types are checked, strict mode rejects undeclared attributes, and a required attribute cannot be deleted from a record.

The API and the CLI accept attribute-level URIs in the same way. xdb records get xdb://com.example/posts/post-123#title returns only that attribute. xdb records delete xdb://com.example/posts/post-123#title --force deletes only that tuple.

  • Records: Groups of tuples with the same path

  • Types: The type system behind tuple values

  • URIs: How tuples are addressed

  • Stores: Tuple-level methods on the store facade

  • Drivers: Tuples as the storage contract