> ## Documentation Index
> Fetch the complete documentation index at: https://www.paradedb.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Indexing Vectors

> Vectors live alongside text and filters in the ParadeDB index

<Note>
  This is a beta feature available in versions `0.25.0` and above. See [How
  Vector Search Works](/docs/documentation/vector/overview) for background.
</Note>

<Note>
  Make sure the [pgvector](https://github.com/pgvector/pgvector) extension is
  installed first. ParadeDB uses pgvector's vector types, but not its HNSW or
  IVF indexes.
</Note>

The ParadeDB index can index pgvector's `vector` type alongside your text and other columns. This lets you combine vector search with full text search and filters in a single index,
which can significantly improve latency/recall for selective queries.

## Create the Index

The `mock_items` table comes with an `embedding` column of type `vector(8)` populated with sample embeddings.
In this example, `embedding` is added to the ParadeDB index with cosine similarity as the distance function.

<CodeGroup>
  ```sql SQL theme={null}
  CREATE INDEX search_idx ON mock_items
  USING paradedb (id, description, category, embedding vector_cosine_ops)
  WITH (key_field='id');
  ```

  ```ts Drizzle theme={null}
  import { indexing } from "@paradedb/drizzle-paradedb";

  // In the pgTable definition:
  (table) => [
    indexing
      .paradedbIndex("search_idx")
      .on(
        table.id,
        table.description,
        table.category,
        indexing.vectorField(table.embedding, "cosine"),
      ),
  ];
  ```

  ```python Django theme={null}
  from django.db import connection
  from paradedb.indexes import ParadeDBIndex

  with connection.schema_editor() as schema_editor:
      schema_editor.add_index(
          MockItem,
          ParadeDBIndex(
              fields={
                  "id": {},
                  "description": {},
                  "category": {},
                  "embedding": {"metric": "cosine"},
              },
              key_field="id",
              name="search_idx",
          ),
      )
  ```

  ```python SQLAlchemy theme={null}
  from sqlalchemy import Index
  from paradedb.sqlalchemy import indexing

  idx = Index(
      "search_idx",
      indexing.ParadeDBField(MockItem.id),
      indexing.ParadeDBField(MockItem.description),
      indexing.ParadeDBField(MockItem.category),
      indexing.VectorField(MockItem.embedding, metric="cosine"),
      postgresql_using="paradedb",
      postgresql_with={"key_field": "id"},
  )

  with engine.begin() as conn:
      idx.create(conn)
  ```

  ```ruby Rails theme={null}
  ActiveRecord::Base.connection.add_paradedb_index(
    :mock_items,
    fields: {
      id: {},
      description: {},
      category: {},
      embedding: { metric: :cosine }
    },
    key_field: :id,
    name: :search_idx
  )
  ```

  ```cs EF Core theme={null}
  modelBuilder.Entity<MockItem>()
      .HasParadeDbIndex("search_idx", e => e.Id)
      .HasField(e => e.Description)
      .HasField(e => e.Category)
      .HasField(e => e.Embedding, VectorMetric.Cosine);
  ```
</CodeGroup>

The desired distance function is encoded into the index definition, and cannot be changed without reindexing.

<CodeGroup>
  ```sql SQL theme={null}
  embedding vector_l2_ops      -- L2 (default)
  embedding vector_cosine_ops  -- cosine
  embedding vector_ip_ops      -- inner product
  ```

  ```ts Drizzle theme={null}
  indexing.vectorField(mockItems.embedding, "l2"); // L2 (default)
  indexing.vectorField(mockItems.embedding, "cosine"); // cosine
  indexing.vectorField(mockItems.embedding, "ip"); // inner product
  ```

  ```python Django theme={null}
  "embedding": {"metric": "l2"}      # L2 (default)
  "embedding": {"metric": "cosine"}  # cosine
  "embedding": {"metric": "ip"}      # inner product
  ```

  ```python SQLAlchemy theme={null}
  indexing.VectorField(MockItem.embedding, metric="l2")      # L2 (default)
  indexing.VectorField(MockItem.embedding, metric="cosine")  # cosine
  indexing.VectorField(MockItem.embedding, metric="ip")      # inner product
  ```

  ```ruby Rails theme={null}
  embedding: { metric: :l2 }      # L2 (default)
  embedding: { metric: :cosine }  # cosine
  embedding: { metric: :ip }      # inner product
  ```

  ```cs EF Core theme={null}
  .HasField(e => e.Embedding, VectorMetric.L2)            // L2 (default)
  .HasField(e => e.Embedding, VectorMetric.Cosine)        // cosine
  .HasField(e => e.Embedding, VectorMetric.InnerProduct)  // inner product
  ```
</CodeGroup>

<Note>
  Only pgvector's `vector` type is supported. The `halfvec`, `sparsevec`, and
  `bit` types are not yet indexable.
</Note>

<Note>
  If you track index build progress with
  [`pg_stat_progress_create_index`](https://www.postgresql.org/docs/current/progress-reporting.html#CREATE-INDEX-PROGRESS-REPORTING),
  you may notice progress appear to "stop" at intervals. This is expected:
  vectors are clustered with k-means at these points, which is computationally
  expensive.
</Note>

## Index Options

ParadeDB uses a SPANN-style vector index, which is similar to an IVF index but with additional structures to improve
recall and latency, especially over large datasets. The following `WITH` options control how vectors are clustered and indexed. All are set at index build time and apply to every vector field in the index. An example:

<CodeGroup>
  ```sql SQL theme={null}
  CREATE INDEX search_idx ON mock_items
  USING paradedb (id, description, category, embedding vector_cosine_ops)
  WITH (key_field='id', centroid_ratio=0.01, training_samples_per_centroid=32, cluster_replication=1);
  ```

  ```ts Drizzle theme={null}
  import { indexing } from "@paradedb/drizzle-paradedb";

  // In the pgTable definition:
  (table) => [
    indexing
      .paradedbIndex("search_idx", {
        centroidRatio: 0.01,
        trainingSamplesPerCentroid: 32,
        clusterReplication: 1,
      })
      .on(
        table.id,
        table.description,
        table.category,
        indexing.vectorField(table.embedding, "cosine"),
      ),
  ];
  ```

  ```python Django theme={null}
  from django.db import connection
  from paradedb.indexes import ParadeDBIndex

  with connection.schema_editor() as schema_editor:
      schema_editor.add_index(
          MockItem,
          ParadeDBIndex(
              fields={
                  "id": {},
                  "description": {},
                  "category": {},
                  "embedding": {"metric": "cosine"},
              },
              key_field="id",
              name="search_idx",
              centroid_ratio=0.01,
              training_samples_per_centroid=32,
              cluster_replication=1,
          ),
      )
  ```

  ```python SQLAlchemy theme={null}
  from sqlalchemy import Index
  from paradedb.sqlalchemy import indexing

  idx = Index(
      "search_idx",
      indexing.ParadeDBField(MockItem.id),
      indexing.ParadeDBField(MockItem.description),
      indexing.ParadeDBField(MockItem.category),
      indexing.VectorField(MockItem.embedding, metric="cosine"),
      postgresql_using="paradedb",
      postgresql_with={
          "key_field": "id",
          **indexing.VectorIndexOptions(
              centroid_ratio=0.01,
              training_samples_per_centroid=32,
              cluster_replication=1,
          ),
      },
  )

  with engine.begin() as conn:
      idx.create(conn)
  ```

  ```ruby Rails theme={null}
  ActiveRecord::Base.connection.add_paradedb_index(
    :mock_items,
    fields: {
      id: {},
      description: {},
      category: {},
      embedding: { metric: :cosine }
    },
    key_field: :id,
    name: :search_idx,
    index_options: {
      centroid_ratio: 0.01,
      training_samples_per_centroid: 32,
      cluster_replication: 1
    }
  )
  ```

  ```cs EF Core theme={null}
  modelBuilder.Entity<MockItem>()
      .HasParadeDbIndex("search_idx", e => e.Id)
      .HasField(e => e.Description)
      .HasField(e => e.Category)
      .HasField(e => e.Embedding, VectorMetric.Cosine)
      .HasCentroidRatio(0.01)
      .HasTrainingSamplesPerCentroid(32)
      .HasClusterReplication(1);
  ```
</CodeGroup>

<ParamField body="centroid_ratio" default={0.01}>
  Vectors are clustered by proximity, and a centroid is a cluster's
  representative vector. This setting controls the number of centroids to build,
  as a fraction of the number of indexed vectors (`num_centroids =
      centroid_ratio * num_vectors`). More centroids produce smaller clusters,
  improving recall at the cost of a slower, more memory-intensive build. Must be
  between `0.000001` and `1.0`.
</ParamField>

<ParamField body="training_samples_per_centroid" default={32}>
  The number of vectors sampled per centroid to train the k-means clustering at
  build time. Higher values yield better-quality centroids at the cost of a
  slower build. Must be between `1` and `100000`.
</ParamField>

<ParamField body="cluster_replication" default={1}>
  The number of clusters each vector is written into: its primary cluster plus
  up to `cluster_replication - 1` next-nearest clusters. Higher values can
  improve recall for filtered queries at the cost of a larger index. The default
  of `1` disables replication.
</ParamField>
