# `Zvex.Collection`
[🔗](https://github.com/edlontech/zvex/blob/main/lib/zvex/collection.ex#L1)

Collection lifecycle management for zvec.

A collection is the fundamental storage unit — it holds typed documents
organized by a `Zvex.Collection.Schema`. Once created and opened, you can
insert, update, upsert, delete, and fetch documents, as well as manage
indexes and columns at runtime.

Every function that can fail returns `{:ok, result} | {:error, Zvex.Error.t()}`
and has a bang (`!`) variant that unwraps or raises.

## Example

    alias Zvex.Collection
    alias Zvex.Collection.Schema

    schema =
      Schema.new("my_collection")
      |> Schema.add_field("id", :string, primary_key: true)
      |> Schema.add_field("embedding", :vector_fp32,
           dimension: 768,
           index: [type: :hnsw, metric: :cosine])

    {:ok, collection} = Collection.create("/tmp/my_collection", schema)
    {:ok, stats} = Collection.stats(collection)
    :ok = Collection.close(collection)

# `t`

```elixir
@type t() :: %Zvex.Collection{path: String.t(), ref: reference()}
```

An open collection handle.

- `:ref` — opaque NIF resource reference to the underlying zvec collection.
- `:path` — filesystem path where the collection data is stored.

Once `close/1` is called, the underlying resource is closed; further
operations through the public API return `{:error, %Zvex.Error.Invalid.FailedPrecondition{}}`.

# `add_column`

```elixir
@spec add_column(t(), String.t(), atom(), keyword()) :: :ok | {:error, Zvex.Error.t()}
```

Adds a new column to the collection schema at runtime.

## Options

- `:nullable` — whether the column allows null values (default `false`)
- `:dimension` — vector dimension, required for vector types (default `0`)
- `:index` — keyword list of index parameters (e.g. `[type: :invert]`)
- `:default` — default expression string applied to existing documents

# `add_column!`

```elixir
@spec add_column!(t(), String.t(), atom(), keyword()) :: :ok
```

Like `add_column/4` but raises on error.

# `alter_column`

```elixir
@spec alter_column(t(), String.t(), keyword()) :: :ok | {:error, Zvex.Error.t()}
```

Alters an existing column.

## Options

- `:new_name` — rename the column
- `:schema` — keyword list with the new field definition (`:name`, `:data_type`,
  `:nullable`, `:dimension`)

# `alter_column!`

```elixir
@spec alter_column!(t(), String.t(), keyword()) :: :ok
```

Like `alter_column/3` but raises on error.

# `close`

```elixir
@spec close(t()) :: :ok | {:error, Zvex.Error.t()}
```

Closes the collection, releasing its native resources.

After closing, all operations on this handle will return an error.

# `close!`

```elixir
@spec close!(t()) :: :ok
```

Like `close/1` but raises on error.

# `create`

```elixir
@spec create(String.t(), Zvex.Collection.Schema.t(), keyword()) ::
  {:ok, t()} | {:error, Zvex.Error.t()}
```

Creates a new collection on disk and opens it.

The `schema` is validated before being sent to the native layer. `opts` are
forwarded as collection-level options (e.g. segment configuration).

Returns `{:ok, collection}` on success.

# `create!`

```elixir
@spec create!(String.t(), Zvex.Collection.Schema.t(), keyword()) :: t()
```

Like `create/3` but raises on error.

# `create_index`

```elixir
@spec create_index(t(), String.t(), keyword()) :: :ok | {:error, Zvex.Error.t()}
```

Creates an index on `field_name`.

`opts` are index-specific parameters (e.g. `type`, `metric`, `m`,
`ef_construction`). See `Zvex.Collection.Schema.IndexParams` for the
full list of supported options.

# `create_index!`

```elixir
@spec create_index!(t(), String.t(), keyword()) :: :ok
```

Like `create_index/3` but raises on error.

# `delete`

```elixir
@spec delete(t(), [String.t()]) ::
  {:ok, %{success: non_neg_integer(), errors: non_neg_integer()}}
  | {:error, Zvex.Error.t()}
```

Deletes documents by their primary keys.

Returns a summary with `:success` and `:errors` counts.

# `delete!`

```elixir
@spec delete!(t(), [String.t()]) :: %{
  success: non_neg_integer(),
  errors: non_neg_integer()
}
```

Like `delete/2` but raises on error.

# `delete_by_filter`

```elixir
@spec delete_by_filter(t(), String.t()) :: :ok | {:error, Zvex.Error.t()}
```

Deletes all documents matching a filter expression.

The `filter` is a zvec filter expression string (same syntax as
`Zvex.Query.filter/2`).

# `delete_by_filter!`

```elixir
@spec delete_by_filter!(t(), String.t()) :: :ok
```

Like `delete_by_filter/2` but raises on error.

# `delete_with_results`

```elixir
@spec delete_with_results(t(), [String.t()]) ::
  {:ok, [map()]} | {:error, Zvex.Error.t()}
```

Deletes documents and returns per-document result details. See `insert_with_results/2`.

# `delete_with_results!`

```elixir
@spec delete_with_results!(t(), [String.t()]) :: [map()]
```

Like `delete_with_results/2` but raises on error.

# `drop`

```elixir
@spec drop(t()) :: :ok | {:error, Zvex.Error.t()}
```

Closes the collection (if open) and deletes its data directory from disk.

This is a destructive, irreversible operation.

# `drop!`

```elixir
@spec drop!(t()) :: :ok
```

Like `drop/1` but raises on error.

# `drop_column`

```elixir
@spec drop_column(t(), String.t()) :: :ok | {:error, Zvex.Error.t()}
```

Removes a column from the collection schema. Existing data for this column is discarded.

# `drop_column!`

```elixir
@spec drop_column!(t(), String.t()) :: :ok
```

Like `drop_column/2` but raises on error.

# `drop_index`

```elixir
@spec drop_index(t(), String.t()) :: :ok | {:error, Zvex.Error.t()}
```

Removes the index from `field_name`.

# `drop_index!`

```elixir
@spec drop_index!(t(), String.t()) :: :ok
```

Like `drop_index/2` but raises on error.

# `fetch`

```elixir
@spec fetch(t(), [String.t()]) ::
  {:ok, [Zvex.Document.t()]} | {:error, Zvex.Error.t()}
```

Fetches documents by their primary keys.

Returns a list of `Zvex.Document` structs in the same order as the requested
keys. Missing keys are silently skipped.

# `fetch!`

```elixir
@spec fetch!(t(), [String.t()]) :: [Zvex.Document.t()]
```

Like `fetch/2` but raises on error.

# `field_names`

```elixir
@spec field_names(t()) :: {:ok, [String.t()]} | {:error, Zvex.Error.t()}
```

Returns the names of all fields. Shorthand for `field_names(collection, :all)`.

# `field_names`

```elixir
@spec field_names(t(), :all | :forward | :vector | :indexed) ::
  {:ok, [String.t()]} | {:error, Zvex.Error.t()}
```

Returns the names of fields matching `category`.

Categories:
- `:all` — every field in the schema
- `:forward` — scalar/forward-stored fields
- `:vector` — vector fields (dense and sparse)
- `:indexed` — fields that have an index

# `field_names!`

```elixir
@spec field_names!(t()) :: [String.t()]
```

Like `field_names/1` but raises on error.

# `field_names!`

```elixir
@spec field_names!(t(), :all | :forward | :vector | :indexed) :: [String.t()]
```

Like `field_names/2` but raises on error.

# `flush`

```elixir
@spec flush(t()) :: :ok | {:error, Zvex.Error.t()}
```

Flushes buffered writes to persistent storage.

# `flush!`

```elixir
@spec flush!(t()) :: :ok
```

Like `flush/1` but raises on error.

# `has_field?`

```elixir
@spec has_field?(t(), String.t()) :: boolean()
```

Returns `true` if the collection schema contains a field named `field_name`.

# `has_index?`

```elixir
@spec has_index?(t(), String.t()) :: boolean()
```

Returns `true` if the field `field_name` has an index.

# `insert`

```elixir
@spec insert(t(), Zvex.Document.t() | [Zvex.Document.t()]) ::
  {:ok, %{success: non_neg_integer(), errors: non_neg_integer()}}
  | {:error, Zvex.Error.t()}
```

Inserts one or more documents into the collection.

Accepts a single `Zvex.Document` or a list. Returns a summary map with
`:success` and `:errors` counts. Documents whose primary key already exists
will be counted as errors.

# `insert!`

```elixir
@spec insert!(t(), Zvex.Document.t() | [Zvex.Document.t()]) :: %{
  success: non_neg_integer(),
  errors: non_neg_integer()
}
```

Like `insert/2` but raises on error.

# `insert_with_results`

```elixir
@spec insert_with_results(t(), Zvex.Document.t() | [Zvex.Document.t()]) ::
  {:ok, [map()]} | {:error, Zvex.Error.t()}
```

Inserts documents and returns per-document result details.

Unlike `insert/2` which returns aggregate counts, this returns a list of
result maps — one per document — with individual status information.

# `insert_with_results!`

```elixir
@spec insert_with_results!(t(), Zvex.Document.t() | [Zvex.Document.t()]) :: [map()]
```

Like `insert_with_results/2` but raises on error.

# `open`

```elixir
@spec open(
  String.t(),
  keyword()
) :: {:ok, t()} | {:error, Zvex.Error.t()}
```

Opens an existing collection from `path`.

The collection must have been previously created with `create/3`. Accepts
the same `opts` as `create/3` for overriding collection-level options.

# `open!`

```elixir
@spec open!(
  String.t(),
  keyword()
) :: t()
```

Like `open/2` but raises on error.

# `optimize`

```elixir
@spec optimize(t()) :: :ok | {:error, Zvex.Error.t()}
```

Triggers index optimization on the collection.

This merges segments and rebuilds indexes for better query performance.
Can be a long-running operation on large collections.

# `optimize!`

```elixir
@spec optimize!(t()) :: :ok
```

Like `optimize/1` but raises on error.

# `options`

```elixir
@spec options(t()) :: {:ok, map()} | {:error, Zvex.Error.t()}
```

Returns the collection-level options as a map.

# `options!`

```elixir
@spec options!(t()) :: map()
```

Like `options/1` but raises on error.

# `schema`

```elixir
@spec schema(t()) :: {:ok, Zvex.Collection.Schema.t()} | {:error, Zvex.Error.t()}
```

Returns the `Zvex.Collection.Schema` of the open collection.

# `schema!`

```elixir
@spec schema!(t()) :: Zvex.Collection.Schema.t()
```

Like `schema/1` but raises on error.

# `stats`

```elixir
@spec stats(t()) :: {:ok, Zvex.Collection.Stats.t()} | {:error, Zvex.Error.t()}
```

Returns a `Zvex.Collection.Stats` struct with the document count and index information.

# `stats!`

```elixir
@spec stats!(t()) :: Zvex.Collection.Stats.t()
```

Like `stats/1` but raises on error.

# `update`

```elixir
@spec update(t(), Zvex.Document.t() | [Zvex.Document.t()]) ::
  {:ok, %{success: non_neg_integer(), errors: non_neg_integer()}}
  | {:error, Zvex.Error.t()}
```

Updates existing documents in the collection.

Documents are matched by primary key. Returns a summary with `:success`
and `:errors` counts. Documents whose primary key does not exist will be
counted as errors.

# `update!`

```elixir
@spec update!(t(), Zvex.Document.t() | [Zvex.Document.t()]) :: %{
  success: non_neg_integer(),
  errors: non_neg_integer()
}
```

Like `update/2` but raises on error.

# `update_with_results`

```elixir
@spec update_with_results(t(), Zvex.Document.t() | [Zvex.Document.t()]) ::
  {:ok, [map()]} | {:error, Zvex.Error.t()}
```

Updates documents and returns per-document result details. See `insert_with_results/2`.

# `update_with_results!`

```elixir
@spec update_with_results!(t(), Zvex.Document.t() | [Zvex.Document.t()]) :: [map()]
```

Like `update_with_results/2` but raises on error.

# `upsert`

```elixir
@spec upsert(t(), Zvex.Document.t() | [Zvex.Document.t()]) ::
  {:ok, %{success: non_neg_integer(), errors: non_neg_integer()}}
  | {:error, Zvex.Error.t()}
```

Inserts or updates documents, depending on whether their primary key exists.

Combines the semantics of `insert/2` and `update/2`. Returns a summary
with `:success` and `:errors` counts.

# `upsert!`

```elixir
@spec upsert!(t(), Zvex.Document.t() | [Zvex.Document.t()]) :: %{
  success: non_neg_integer(),
  errors: non_neg_integer()
}
```

Like `upsert/2` but raises on error.

# `upsert_with_results`

```elixir
@spec upsert_with_results(t(), Zvex.Document.t() | [Zvex.Document.t()]) ::
  {:ok, [map()]} | {:error, Zvex.Error.t()}
```

Upserts documents and returns per-document result details. See `insert_with_results/2`.

# `upsert_with_results!`

```elixir
@spec upsert_with_results!(t(), Zvex.Document.t() | [Zvex.Document.t()]) :: [map()]
```

Like `upsert_with_results/2` but raises on error.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
