# Vector schema annotations

> **CocoIndex v1, Rust SDK (experimental).** This page documents the CocoIndex **v1** Rust SDK (the `cocoindex` crate). The Rust SDK is experimental: any release may change or remove its API, and it is not yet published on crates.io. The Python SDK pages are at https://cocoindex.io/docs/.
>
> Source: https://cocoindex.io/docs/rust/common_resources/vector_schema/ · Rust docs index: https://cocoindex.io/docs/rust/llms.txt · Rust SDK tour: https://github.com/cocoindex-io/cocoindex/blob/main/rust/sdk/SHOWCASE.md · Rust examples: https://github.com/cocoindex-io/cocoindex/tree/main/examples/rust

The schema module (`cocoindex::resources::schema`, re-exported from `cocoindex::prelude`) defines types that describe vector columns. CocoIndex connectors use these to automatically configure the correct column type (e.g., `vector(384)` in Postgres, `fixed_size_list<float32>(384)` in LanceDB).

A vector schema is not attached to the row type with an annotation. A row struct field is marked `#[coco(vector)]` (or `#[coco(vector = N)]`), and the connector's `TableSchema` resolves its dimension.

## VectorSchema

A struct that describes a vector column's dtype and dimension.

```rust
use cocoindex::prelude::*;

let schema = VectorSchema::f32(768);
// A half-precision column, where the store has one (halfvec in Postgres, float16 in LanceDB).
let half = VectorSchema::f16(768);
// The same, written out.
let schema = VectorSchema { element_type: VectorElementType::Float32, size: 768 };
```

**Fields:**

- `element_type` — A `VectorElementType`: `Float32`, `Float16` or `Float64`. `Float16` selects a half-precision column where the store has one; `Float32` and `Float64` both select the store's standard float vector column.
- `size` — Number of dimensions in the vector (e.g., `384`)

You can construct `VectorSchema` directly when using a custom embedding model that doesn't implement `VectorSchemaProvider`:

```rust
use cocoindex::connectors::qdrant::{self, CollectionSchema, Distance};
use cocoindex::prelude::*;

cocoindex::context_key!(static DB: qdrant::QdrantConnection);

// For a custom CLIP model with known dimension
let schema = VectorSchema::f32(768);

// Use it in a Qdrant collection schema
let target = qdrant::mount_collection_target(
    &ctx,
    &DB,
    "image_search",
    CollectionSchema::from_vector_schema(schema, Distance::Cosine)?,
)
.await?;
```

## VectorSchemaProvider

A trait for values that can provide vector schema information, with one method, `async fn vector_schema(&self) -> Result<VectorSchema>`. The built-in [`SentenceTransformerEmbedder`](/docs/rust/ops/sentence_transformers/), [`ApiEmbedder`](/docs/rust/ops/model_apis/) and `VectorSchema` itself implement it.

A row struct does not carry the provider: a field marked `#[coco(vector)]` has a dimension that is "unresolved" until the connector schema's `with_vector_schema(field, &provider)` (or `with_vector_dim(field, n)`) supplies it. Mounting a target with an unresolved vector column is an error that names the field and `with_vector_dim`.

There are three ways to size a vector field:

### Using an embedder from the context (recommended)

Define a [context key](/docs/rust/programming_guide/context/) for the embedder, resolve it with `ctx.get_key(&EMBEDDER)?` when you mount the target, and pass the embedder to `with_vector_schema`. This is the recommended approach because the embedder is configured once in the lifespan and shared across all functions via context.

```rust
use cocoindex::connectors::postgres;
use cocoindex::ops::sentence_transformers::SentenceTransformerEmbedder;
use cocoindex::prelude::*;

const EMBED_MODEL: &str = "sentence-transformers/all-MiniLM-L6-v2";

cocoindex::context_key!(
    static EMBEDDER: SentenceTransformerEmbedder,
    detect_change
);

#[derive(Clone, Serialize, Deserialize, SchemaFields)]
struct DocEmbedding {
    id: i64,
    text: String,
    #[coco(vector)]
    embedding: Vec<f32>,
}

// In the lifespan, provide the embedder:
#[cocoindex::lifespan]
async fn lifespan(builder: EnvironmentBuilder) -> Result<EnvironmentBuilder> {
    let embedder = SentenceTransformerEmbedder::load(EMBED_MODEL).await?;
    Ok(builder.provide_key(&EMBEDDER, embedder))
}

// When mounting the target, the embedder reports the dimension of the `embedding` column:
let table = postgres::mount_table_target(
    &ctx,
    &DB,
    "doc_embeddings",
    postgres::TableSchema::from_row::<DocEmbedding>(["id"])?
        .with_vector_schema("embedding", ctx.get_key(&EMBEDDER)?)
        .await?,
    Some("public"),
)
.await?;

// In functions, access the embedder:
let embedding = ctx.get_key(&EMBEDDER)?.embed(ctx, text).await?;
```

### Using a `VectorSchemaProvider` instance

Pass an embedder instance directly to `with_vector_schema`. Simpler for scripts where the embedder is a module-level constant.

```rust
let embedder = SentenceTransformerEmbedder::load("sentence-transformers/all-MiniLM-L6-v2").await?;

let schema = postgres::TableSchema::from_row::<DocEmbedding>(["id"])?
    .with_vector_schema("embedding", &embedder) // dimension read from the model (384)
    .await?;
```

`with_vector_schema` takes any `VectorSchemaProvider` by reference. The Postgres, SQLite, Doris, LanceDB and SurrealDB schemas have it; Qdrant's `CollectionSchema::from_row`, Turbopuffer and zvec take `with_vector_dim`.

### Using a `VectorSchema`

Specify dimension and dtype explicitly. Useful when using a custom embedding model that doesn't implement `VectorSchemaProvider`.

```rust
#[derive(Clone, Serialize, Deserialize, SchemaFields)]
struct ImageEmbedding {
    id: i64,
    #[coco(vector = 768)]
    embedding: Vec<f32>,
}
```

`#[coco(vector = N)]` fixes an `f32` column of dimension `N` for every connector the struct is used with. `#[coco(vector = N, half)]` makes it a half-precision column. For a dimension known only at run time, keep `#[coco(vector)]` and call `with_vector_dim("embedding", n)?` on the connector schema, or pass a fixed `VectorSchema` to `with_vector_schema`, since `VectorSchema` is its own provider.

A connector's `TableSchema::from_row::<Row>(primary_key)` reads the `#[derive(SchemaFields)]` row struct. A `#[coco(vector = N)]` field is complete; a `#[coco(vector)]` field waits for `with_vector_schema` or `with_vector_dim`.

## MultiVectorSchema / MultiVectorSchemaProvider

Analogous types for multi-vector columns (e.g., ColBERT-style token-level embeddings). `MultiVectorSchema` wraps a `VectorSchema` describing the individual vectors. Used by connectors like [Qdrant](/docs/rust/connectors/qdrant/) that support multi-vector storage.

```rust
use cocoindex::connectors::qdrant::{CollectionSchema, Distance};
use cocoindex::prelude::*;

let multi_schema = MultiVectorSchema {
    vector_schema: VectorSchema::f32(128),
};
let collection = CollectionSchema::from_multivector_schema(multi_schema, Distance::Cosine)?;
```

`MultiVectorSchemaProvider` is the trait form (`async fn multi_vector_schema(&self) -> Result<MultiVectorSchema>`), implemented by `MultiVectorSchema` itself; Qdrant's `CollectionSchema::from_multivector_provider(&provider, distance)` accepts one.
