---
title: "How do I migrate from pgvector to an embedding index on the table?"
description: "Leave pgvector by copying source text (not the vector column), declaring an embedding index, and ranking with similarity(string=). Rebuild, do not COPY vectors."
url: "https://www.pixeltable.com/learn/how-to/migrate-from-pgvector"
updated: "2026-10-28"
doc: "https://docs.pixeltable.com/howto/coming-from"
---

# How do I migrate from pgvector to an embedding index on the table?

pgvector stores vectors next to rows in Postgres. Moving off it means keeping those rows and letting an embedding index on the source column replace the vector column and the ANN query you wrote. You still need the original text or image. A COPY of the vector column without the source cannot rebuild search under a new model.

Updated: 2026-10-28
Part of [How do I ... in Python?](https://www.pixeltable.com/learn/how-to).

## Before you start

About 20 min.

- Python 3.11+
- pip install -U 'pixeltable[serve]'
- pxt init in the project root
- sentence-transformers for all-MiniLM-L6-v2

## On this page

- [Before you start](https://www.pixeltable.com/learn/how-to/migrate-from-pgvector#before-you-start)
- [Copy the source column, not the vector column](https://www.pixeltable.com/learn/how-to/migrate-from-pgvector#step-1)
- [Replace the vector column with an embedding index](https://www.pixeltable.com/learn/how-to/migrate-from-pgvector#step-2)
- [Insert the copied rows](https://www.pixeltable.com/learn/how-to/migrate-from-pgvector#step-3)
- [Rank with similarity instead of an ANN SQL operator](https://www.pixeltable.com/learn/how-to/migrate-from-pgvector#step-4)
- [Comparison](https://www.pixeltable.com/learn/how-to/migrate-from-pgvector#comparison)
- [Where Pixeltable fits](https://www.pixeltable.com/learn/how-to/migrate-from-pgvector#where-pixeltable-fits)
- [Questions](https://www.pixeltable.com/learn/how-to/migrate-from-pgvector#questions)

## 1. Copy the source column, not the vector column {#step-1}

SELECT the id, the text (or image URI), and filters. Leave the vector column behind. If you only have vectors, you cannot switch models later without the source.

```python
import pixeltable as pxt
from pixeltable.functions.huggingface import sentence_transformer

TableModel = pxt.model_base()
text_embed = sentence_transformer.using(model_id='all-MiniLM-L6-v2')

class Notes(TableModel, name='notes'):
    pg_id: pxt.String
    content: pxt.String
    tenant: pxt.String | None
```

## 2. Replace the vector column with an embedding index {#step-2}

The index lives on content. cosine is the default metric. You do not declare a vector(n) column or write CREATE INDEX ON embedding USING ivfflat.

```python
class Notes(TableModel, name='notes'):
    pg_id: pxt.String
    content: pxt.String
    tenant: pxt.String | None
    __indexes__ = [
        pxt.EmbeddingIndex(content, string_embed=text_embed, metric='cosine'),
    ]

# pxt schema update app.py pgvector_notes -f
```

## 3. Insert the copied rows {#step-3}

Load the SELECT result (CSV, Parquet, or dicts) and insert. Each row is embedded. Tenant stays a filter column, not a separate table.

```python
notes = pxt.get_table('pgvector_notes.notes')
notes.insert([
    {
        'pg_id': '42',
        'content': 'Theodore Roosevelt won the 1904 US presidential election.',
        'tenant': 'history',
    },
])
```

## 4. Rank with similarity instead of an ANN SQL operator {#step-4}

where() restricts tenant. similarity(string=) replaces <=> or cosine_distance in SQL. Limit is the number of neighbors.

```python
@pxt.query
def search_notes(query: str, tenant: str, limit: int = 5):
    sim = Notes.content.similarity(string=query)
    return (
        Notes.where(Notes.tenant == tenant)
        .order_by(sim, asc=False)
        .limit(limit)
        .select(Notes.pg_id, Notes.content, sim)
    )
```

## pgvector column versus an index on the table {#comparison}

| Job | pgvector in Postgres | Index on the table |
| --- | --- | --- |
| Vector storage | A vector(n) column you write | EmbeddingIndex on the source column |
| Insert | You embed, then INSERT the array | Insert the text; the index embeds |
| Query | ORDER BY embedding <=> $1 | column.similarity(string=) |
| New model | Rewrite the column and the index | A destructive schema update rebuilds |
| Keep it when | Postgres ANN is already the contract | The source row should own search |

## Where Pixeltable fits {#where-pixeltable-fits}

Pixeltable vs Supabase covers pgvector as a Postgres feature, not a separate product page. Copy source text, declare EmbeddingIndex, and drop the vector column and the ANN SQL. Keep pgvector when that engine is already the serving contract. Coming from another stack is the docs hub.

```python
import pixeltable as pxt
from pixeltable.functions.huggingface import sentence_transformer

TableModel = pxt.model_base()
text_embed = sentence_transformer.using(model_id='all-MiniLM-L6-v2')

class Notes(TableModel, name='notes'):
    pg_id: pxt.String
    content: pxt.String
    tenant: pxt.String | None
    __indexes__ = [
        pxt.EmbeddingIndex(content, string_embed=text_embed, metric='cosine'),
    ]

@pxt.query
def search_notes(query: str, tenant: str, limit: int = 5):
    sim = Notes.content.similarity(string=query)
    return (
        Notes.where(Notes.tenant == tenant)
        .order_by(sim, asc=False)
        .limit(limit)
        .select(Notes.pg_id, Notes.content, sim)
    )

# pxt schema update app.py pgvector_notes -f
```

## Concepts

- [What is semantic search?](https://www.pixeltable.com/learn/what-is-semantic-search)
- [How does an embedding index differ from a vector database?](https://www.pixeltable.com/learn/embedding-index-vs-vector-database)
- [What is an embedding index?](https://www.pixeltable.com/learn/what-is-an-embedding-index)

## Use case

- [Multimodal AI Apps: Process Any Data Type in One System](https://www.pixeltable.com/use-cases/multimodal-ai-application-development)

## Questions {#questions}

### Can I COPY the vector column across? {#faq-1}

Do not. COPY the source text (and filters). The embedding index rebuilds vectors. A copied array still needs the original model at query time and cannot follow a model switch.

### Does this use IVFFlat or HNSW in Postgres? {#faq-2}

No. Those are pgvector index types you create in SQL. This page replaces that column with an embedding index on the source field.

### When do I keep pgvector? {#faq-3}

When Postgres ANN is already the serving contract and other SQL joins depend on that column. Pixeltable vs Supabase is the page for that engine.

## Related

- [What is semantic search?](https://www.pixeltable.com/learn/what-is-semantic-search)
- [Embedding index vs a vector database](https://www.pixeltable.com/learn/embedding-index-vs-vector-database)
- [What is an embedding index?](https://www.pixeltable.com/learn/what-is-an-embedding-index)
- [How do I do semantic search in Python?](https://www.pixeltable.com/learn/how-to/semantic-search-in-python)
- [How do I migrate from Pinecone?](https://www.pixeltable.com/learn/how-to/migrate-from-pinecone)
- [Pixeltable vs Pinecone](https://www.pixeltable.com/compare/pixeltable-vs-pinecone)
- [Pixeltable vs Supabase](https://www.pixeltable.com/compare/pixeltable-vs-supabase)
- [Multimodal application development](https://www.pixeltable.com/use-cases/multimodal-ai-application-development)
- [Migrate to Pixeltable](https://docs.pixeltable.com/howto/coming-from)
- [Embedding indices](https://docs.pixeltable.com/platform/embedding-indexes)
