# `Concord.Index`
[🔗](https://github.com/gsmlg-dev/concord/blob/main/lib/concord/index.ex#L1)

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)

## Declarative Extractor Specs (Recommended)

    # 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)

# `extractor`

```elixir
@type extractor() :: Concord.Index.Extractor.spec() | (term() -&gt; term())
```

Extractor: declarative spec or legacy function

# `index_name`

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

Index name (unique identifier)

# `index_value`

```elixir
@type index_value() :: term()
```

Value to index (must be comparable)

# `create`

```elixir
@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).

## Declarative Specs (Recommended)

    :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`

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

Drops an existing secondary index.

# `list`

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

Lists all secondary indexes.

# `lookup`

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

Looks up keys by indexed value.

# `reindex`

```elixir
@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.

