Concord.Index (Concord v2.0.0)

Copy Markdown View Source

Secondary index support for efficient value-based queries.

Secondary indexes enable fast lookups by specific value fields without scanning all keys. Each index maintains a mapping from indexed values to the keys that contain those values.

Features

  • Automatic Maintenance: Indexes update automatically on put/delete
  • Multiple Indexes: Support for multiple indexes per store
  • Declarative Extractors: Define indexes with data specs (safe for Raft replication)
  • Backward Compatible: Anonymous functions still accepted during migration
  • Efficient Lookups: O(1) lookup by indexed value
  • Multi-value Support: Index multiple values per key (e.g., tags)
# Index on a map key
:ok = Concord.Index.create("users_by_email", {:map_get, :email})

# Index on a nested path
:ok = Concord.Index.create("by_city", {:nested, [:address, :city]})

# Index on the raw value
:ok = Concord.Index.create("by_value", {:identity})

Legacy Function Extractors (Deprecated)

# Still works but stores anonymous functions in Raft log — unsafe across upgrades
:ok = Concord.Index.create("by_email", fn u -> u.email end)

Summary

Types

Extractor: declarative spec or legacy function

Index name (unique identifier)

Value to index (must be comparable)

Functions

Creates a new secondary index.

Drops an existing secondary index.

Lists all secondary indexes.

Looks up keys by indexed value.

Rebuilds an index from all existing keys. Uses the declarative extractor spec stored in the state machine.

Types

extractor()

@type extractor() :: Concord.Index.Extractor.spec() | (term() -> term())

Extractor: declarative spec or legacy function

index_name()

@type index_name() :: String.t()

Index name (unique identifier)

index_value()

@type index_value() :: term()

Value to index (must be comparable)

Functions

create(name, extractor, opts \\ [])

@spec create(index_name(), extractor(), keyword()) :: :ok | {:error, term()}

Creates a new secondary index.

Accepts either a declarative extractor spec (recommended) or a legacy anonymous function (deprecated — unsafe across code upgrades).

:ok = Concord.Index.create("by_email", {:map_get, :email})
:ok = Concord.Index.create("by_city", {:nested, [:address, :city]})
:ok = Concord.Index.create("by_value", {:identity})
:ok = Concord.Index.create("by_first", {:element, 0})

Options

  • :reindex - If true, reindex all existing keys (default: false)
  • :timeout - Operation timeout in milliseconds (default: 5000)

drop(name, opts \\ [])

@spec drop(
  index_name(),
  keyword()
) :: :ok | {:error, term()}

Drops an existing secondary index.

list(opts \\ [])

@spec list(keyword()) :: {:ok, [String.t()]} | {:error, term()}

Lists all secondary indexes.

lookup(name, value, opts \\ [])

@spec lookup(index_name(), index_value(), keyword()) ::
  {:ok, [String.t()]} | {:error, term()}

Looks up keys by indexed value.

reindex(name, opts \\ [])

@spec reindex(
  index_name(),
  keyword()
) :: :ok | {:error, term()}

Rebuilds an index from all existing keys. Uses the declarative extractor spec stored in the state machine.