View Source Dynamo.Schema (Dynamo v0.2.0)

Defines a DynamoDB item schema with typed fields, keys and indexes.

defmodule MyApp.Order do
  use Dynamo.Schema

  item do
    table "app_data"
    entity_name "order"            # prefix used in generated keys (default: module basename)

    field :user_id, :string, partition_key: true
    field :placed_at, :utc_datetime, sort_key: true
    field :order_id, :string, sort_key: true
    field :total, :decimal
    field :status, :string, default: "pending"
    field :tags, :string_set

    global_secondary_index "StatusIndex", partition_key: :status, sort_key: :placed_at
  end
end

Fields

field name, type \\ :string, opts. Types are the built-ins listed in Dynamo.Type or a module implementing Dynamo.Type. Options:

  • :default – struct default
  • :partition_key / :sort_key – include this field in the composite key (in declaration order); alternatively use partition_key [...] / sort_key [...].
  • :required – dump/1 returns an error when the value is nil

Key generation

The table's partition/sort key attributes are named by the partition_key_name / sort_key_name options (default "pk" / "sk"). Their values are built by joining the dumped, stringified key field values with key_separator (default "#"):

pk = "<entity_name><sep><f1><sep><f2>…"
sk = "<f1><sep><f2>…"             (with `prefix_sort_key: true`: "<name1><sep><v1><sep>…")

Every partition-key field must be non-nil – a nil produces a {:error, %Dynamo.Error{type: :validation_error}}. Sort-key fields may be partially populated: generation stops at the first nil, which yields a prefix usable with begins_with queries. Separator characters inside key values are rejected so keys always round-trip.

Set raw_keys: true to use the first partition/sort key field as is (no entity prefix, no joining) – for tables not designed around this library. In that mode the key attribute names default to the field names.

Options (to use Dynamo.Schema)

  • :partition_key_name, :sort_key_name – attribute names (default "pk"/"sk")
  • :key_separator – default "#"
  • :prefix_sort_key – default false
  • :raw_keys – default false
  • :omit_nil – omit nil fields from written items (default true)

Embedded schemas

A schema module can be used as a field type. Declare it with use Dynamo.Schema, embedded: true (no table, keys or indexes) and nest it directly or inside a typed list:

defmodule MyApp.Address do
  use Dynamo.Schema, embedded: true
  item do
    field :street, :string
    field :city, :string
    field :geo, {:list, :float}
  end
end

field :address, MyApp.Address
field :previous_addresses, {:list, MyApp.Address}

Embedded values round-trip with full typing (dates stay dates, decimals stay decimals). Any table schema can also be embedded; its key attributes are not included when nested.

Generated functions

__schema__/1 (:table, :fields, :field_types, :partition_key, :sort_key, :indexes, :vector_indexes, :options, :entity_name), __schema__/2 ({:type, field}, {:index, name}, {:vector_index, name}).

Summary

Functions

Raises unless module is a schema.

Dumps a struct into a DynamoDB item map, including the generated key attributes. Nil fields are omitted when omit_nil is true.

Like dump/1 but raises.

Dumps only the declared fields (no generated key attributes).

Sets the entity prefix used in generated partition keys.

Resolves the key for a global secondary index. The GSI partition key field must be non-nil; the sort key is nil when unpopulated. Values are dumped with their field types (so numeric GSI keys stay N).

Defines the item. Must contain exactly one table and at least one partition key field.

Returns the {partition_key_name, sort_key_name} attribute names for the schema's table. sort_key_name is nil when the schema has no sort key.

Loads a DynamoDB item map into a struct. Unknown attributes (including the generated key attributes) are ignored. Returns {:error, %Dynamo.Error{}} when a value cannot be loaded with the field's type.

Like load/2 but raises.

Loads a list of items, stopping at the first error.

Computes the partition key attribute value for a struct. All partition key fields must be non-nil.

Resolves the table primary key for a struct.

Returns true if module was defined with Dynamo.Schema.

Computes the sort key attribute value for a struct, or {:ok, nil} when the schema has no sort key or no sort key field is populated. Stops at the first nil field (partial sort key).

Sets the table name. Accepts a literal or any expression evaluated at compile time, e.g. table Application.compile_env!(:my_app, :orders_table) or table "orders_" <> System.get_env("ENV", "dev").

Declares a DynamoDB vector index over a :vector field.

Functions

assert_schema!(module)

Raises unless module is a schema.

dump(struct)

@spec dump(struct()) :: {:ok, map()} | {:error, Dynamo.Error.t()}

Dumps a struct into a DynamoDB item map, including the generated key attributes. Nil fields are omitted when omit_nil is true.

dump!(struct)

Like dump/1 but raises.

dump_attributes(struct)

@spec dump_attributes(struct()) :: {:ok, map()} | {:error, Dynamo.Error.t()}

Dumps only the declared fields (no generated key attributes).

entity_name(name)

(macro)

Sets the entity prefix used in generated partition keys.

Accepts a string, or another Dynamo.Schema module whose entity name is inherited at compile time. Use the module form to place child items in a parent's partition without duplicating the string:

defmodule Shop.Order do
  use Dynamo.Schema
  item do
    table "shop"
    entity_name Shop.Customer                            # pk = "customer#<customer_id>"
    field :customer_id, :string, partition_key: true
    field :kind, :string, sort_key: true, default: "order"
    field :order_id, :string, sort_key: true
  end
end

Renaming Shop.Customer then moves both entities together; a typo is a compile error instead of a silently different partition.

field(name)

(macro)

field(name, opts)

(macro)

field(name, type, opts)

(macro)

global_secondary_index(name, opts)

(macro)

index_key(struct, index_name)

@spec index_key(
  struct(),
  String.t()
) :: {:ok, Dynamo.Key.t()} | {:error, Dynamo.Error.t()}

Resolves the key for a global secondary index. The GSI partition key field must be non-nil; the sort key is nil when unpopulated. Values are dumped with their field types (so numeric GSI keys stay N).

item(list)

(macro)

Defines the item. Must contain exactly one table and at least one partition key field.

key_names(mod)

@spec key_names(module()) :: {String.t(), String.t() | nil}

Returns the {partition_key_name, sort_key_name} attribute names for the schema's table. sort_key_name is nil when the schema has no sort key.

load(mod, item)

@spec load(module(), map()) :: {:ok, struct()} | {:error, Dynamo.Error.t()}

Loads a DynamoDB item map into a struct. Unknown attributes (including the generated key attributes) are ignored. Returns {:error, %Dynamo.Error{}} when a value cannot be loaded with the field's type.

load!(mod, item)

Like load/2 but raises.

load_all(mod, items)

@spec load_all(module(), [map()]) :: {:ok, [struct()]} | {:error, Dynamo.Error.t()}

Loads a list of items, stopping at the first error.

partition_key(fields)

(macro)

partition_key_value(struct)

@spec partition_key_value(struct()) :: {:ok, map()} | {:error, Dynamo.Error.t()}

Computes the partition key attribute value for a struct. All partition key fields must be non-nil.

primary_key(struct)

@spec primary_key(struct()) :: {:ok, Dynamo.Key.t()} | {:error, Dynamo.Error.t()}

Resolves the table primary key for a struct.

Returns {:ok, %Dynamo.Key{}} or {:error, %Dynamo.Error{}} when a partition key field is nil or a key value contains the separator.

primary_key!(struct)

Like primary_key/1 but raises.

schema?(module)

@spec schema?(module()) :: boolean()

Returns true if module was defined with Dynamo.Schema.

sort_key(fields)

(macro)

sort_key_value(struct)

@spec sort_key_value(struct()) :: {:ok, map() | nil} | {:error, Dynamo.Error.t()}

Computes the sort key attribute value for a struct, or {:ok, nil} when the schema has no sort key or no sort key field is populated. Stops at the first nil field (partial sort key).

table(name)

(macro)

Sets the table name. Accepts a literal or any expression evaluated at compile time, e.g. table Application.compile_env!(:my_app, :orders_table) or table "orders_" <> System.get_env("ENV", "dev").

vector_index(name, opts)

(macro)

Declares a DynamoDB vector index over a :vector field.

field :embedding, {:vector, 1024}
field :tenant, :string
field :lang, :string

vector_index "Semantic",
  field: :embedding,
  distance: :cosine,                   # :cosine | :dot_product | :euclidean
  projection: :include, projected_attributes: [:title],
  partition_key: :tenant,              # optional; searches must then pass it
  filters: [:lang]                     # optional inline filter attributes

dimensions: defaults to the field's {:vector, n} size. Search with Dynamo.Repo.search_vectors/4. Vector indexes require on-demand billing.