How to

How do I do semantic search in Python?

Semantic search in Python ranks rows by meaning: embed the query and the stored text with the same model, then return nearest neighbors. A vector database in Python is one way to store those vectors. The other is an index on the table that already holds the passages, so insert updates search without a second copy.

Updated · Part of How do I ... in Python?

Before you start

About 15 min.

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

1. Store the passages you will search

Semantic search needs the text on a row, plus any metadata you will filter on. A vector with no source string cannot cite anything.

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'):
title: pxt.String
content: pxt.String
category: pxt.String

2. Declare the embedding index on that column

A vector database in Python is often a second product you upsert into. An embedding index on the column is the same nearest-neighbor search without the copy. Insert a row and the vector follows.

class Notes(TableModel, name='notes'):
title: pxt.String
content: pxt.String
category: pxt.String
__indexes__ = [
pxt.EmbeddingIndex(content, string_embed=text_embed),
]

3. Rank by similarity to a sentence

Embed the question with the same model. order_by the similarity score descending. Limit is the number of neighbors. Keyword search is a different index; combine it only if exact tokens matter.

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

Semantic search versus a vector database

Semantic search versus a vector database
Index on the tableA vector database in Python
StoresThe passage and the vectorThe vector, plus metadata you copy in
InsertThe index updates with the rowAn upsert you remember to run
Querycolumn.similarity(string=)A client against another service
When to splitRare: another system is already source of truthHosted ANN, namespaces, or a query SLA

Where Pixeltable fits

Pixeltable is the unified multimodal backend: EmbeddingIndex on the column that holds the text, then similarity(string=). A vector database in Python is optional. Keep Pinecone or Weaviate when that engine is the serving contract; see the vector database alternatives roundup.

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'):
content: pxt.String
__indexes__ = [
pxt.EmbeddingIndex(content, string_embed=text_embed),
]
@pxt.query
def search_notes(query: str, limit: int = 5):
sim = Notes.content.similarity(string=query)
return Notes.order_by(sim, asc=False).limit(limit).select(Notes.content, sim)

Questions

Is semantic search the same as a vector database?
Semantic search is the ranking. A vector database is one place to store the vectors. The index can also live on the table that holds the passages. You need the ranking; you need a separate database only when that engine is the product.
How is this different from keyword search?
Keyword search matches tokens. Semantic search matches meaning, so a paraphrase can still hit. Exact product names or error codes often want a keyword filter beside the embedding.
Which embedding model should I use?
all-MiniLM-L6-v2 is the documented fast local default. all-mpnet-base-v2 is stronger and slower. Hosted models such as OpenAI text-embedding-3-small are an API. Use the same model at insert and at query.