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

Declarative index extractor specifications.

All specs are plain data (tuples of atoms/binaries/integers) — no anonymous
functions. This makes them safe to serialize in the Raft log and snapshots
across code versions without risk of `:badfun` errors.

## Supported Specs

  * `{:map_get, key}` — Extract a single map key
  * `{:nested, [key1, key2, ...]}` — Extract a nested value via `get_in/2`
  * `{:identity}` — Index the entire value as-is
  * `{:element, n}` — Extract the nth element from a tuple

## Examples

    # Index on user email field
    {:map_get, :email}

    # Index on nested field
    {:nested, [:address, :city]}

    # Index on the raw value
    {:identity}

# `spec`

```elixir
@type spec() ::
  {:map_get, atom() | binary()}
  | {:nested, [atom() | binary()]}
  | {:identity}
  | {:element, non_neg_integer()}
```

# `extract`

```elixir
@spec extract(spec(), term()) :: term() | nil
```

Extracts an index value from a stored value using the given spec.

Returns `nil` if extraction fails or the field doesn't exist.

# `index_value`

```elixir
@spec index_value(atom(), binary(), term(), spec()) :: :ok
```

Indexes a value in the given ETS table using the extractor spec.
Handles single values and lists of values.

# `remove_from_index`

```elixir
@spec remove_from_index(atom(), binary(), term(), spec()) :: :ok
```

Removes a value from the index ETS table using the extractor spec.

# `valid?`

```elixir
@spec valid?(term()) :: boolean()
```

Validates that a given term is a supported extractor spec.

