Vector schema annotations
Describe vector columns with VectorSchema and VectorSchemaProvider. Covers the three ways to size a vector field (an embedder from the context, an embedder instance, an explicit schema) and MultiVectorSchema for models like ColBERT.
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.
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— AVectorElementType:Float32,Float16orFloat64.Float16selects a half-precision column where the store has one;Float32andFloat64both 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:
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, ApiEmbedder 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 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.
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.
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.
#[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 that support multi-vector storage.
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.