Mosaic AI Vector Search in Python: Create, Query and Filter an Index
Databricks GenAI Engineer · Deep dive

Mosaic AI Vector Search in Python: Create, Query and Filter an Index

In Mosaic AI Vector Search, similarity_search is the Python method that queries an index: it takes a query text or vector, the columns to return, a result count, optional filters and a query type (ANN or HYBRID). Filters are a dictionary on Standard endpoints and a SQL-like string on Storage Optimized endpoints.

Last updated November 2026.

What this deep dive adds

Chapter 33 of the course builds one Delta Sync index and runs one query with a single equality filter. This post is the reference behind that chapter: complete create code for all three index flavors, keeping an index current, every similarity_search argument, the full filter syntax on both endpoint types, the reranker, and the retriever tool an agent calls.

"Create and query a Vector Search index" is an objective in Section 4, Assembling and Deploying Applications, of the 18 March 2026 exam guide. The concepts around it (endpoints, index types, sizing) are in the Vector Search and RAG post.

Names in the code: the docs renamed the product AI Search in June 2026, and the current SDK is databricks-ai-search with AISearchClient. VectorSearchClient is the same class under its old name, and databricks-vectorsearch is now a deprecated shim over it. The exam guide, and this post, say Vector Search.

Create each index flavor

A Standard endpoint and a Delta Sync index whose embeddings Databricks computes:

%pip install databricks-ai-search
dbutils.library.restartPython()

from databricks.ai_search.client import AISearchClient

client = AISearchClient()
client.create_endpoint(name="rag_endpoint", endpoint_type="STANDARD")

index = client.create_delta_sync_index(
    endpoint_name="rag_endpoint",
    source_table_name="main.rag.doc_chunks",
    index_name="main.rag.doc_chunks_index",
    pipeline_type="TRIGGERED",
    primary_key="chunk_id",
    embedding_source_column="chunk_text",
    embedding_model_endpoint_name="databricks-qwen3-embedding-0-6b",
    columns_to_sync=["chunk_text", "source_url", "region", "doc_year"],
)

Databricks embeds chunk_text through the named endpoint at sync time, and each query_text with the same model. columns_to_sync matters more than it looks: by default every source column is synced, but when set, only those columns plus the primary key and embedding column are indexed, and only indexed columns can be returned or filtered on. A source_url left out here never comes back.

When an upstream pipeline already writes vectors, point the index at that column instead:

index = client.create_delta_sync_index(
    endpoint_name="rag_endpoint",
    source_table_name="main.rag.doc_chunks_embedded",
    index_name="main.rag.doc_chunks_embedded_index",
    pipeline_type="TRIGGERED",
    primary_key="chunk_id",
    embedding_dimension=1024,
    embedding_vector_column="chunk_vector",
)

The vector column is array<float> and embedding_dimension must match it (on Storage Optimized endpoints the dimension must divide evenly by 16). Queries now pass query_vector, embedded by the same model you used upstream.

A Direct Vector Access index has no source table, so you declare its schema yourself. It cannot be created in the UI, only through the SDK or REST API:

index = client.create_direct_access_index(
    endpoint_name="rag_endpoint",
    index_name="main.rag.live_notes_index",
    primary_key="note_id",
    embedding_dimension=1024,
    embedding_vector_column="note_vector",
    schema={
        "note_id": "int",
        "note_text": "string",
        "region": "string",
        "note_vector": "array<float>",
    },
)
How data reaches each index flavor Delta Sync with managed embeddings: a Delta table's text column goes through an embedding endpoint into the index, and queries use query_text. Delta Sync with self-managed embeddings: a Delta table that already holds a vector column syncs into the index, and queries use query_vector. Direct Vector Access: your code calls upsert and delete on the index, with no source table and no sync, and queries use query_vector. DELTA SYNC, MANAGED EMBEDDINGS Delta table chunk_text Embedding endpoint Index Query with query_text; Databricks embeds it DELTA SYNC, SELF-MANAGED Delta table chunk_vector sync no embedding Index Query with query_vector from your model DIRECT VECTOR ACCESS Your code no source table upsert() delete() Index Query with query_vector; no sync to run
Who computes the vectors decides whether a query passes query_text or query_vector.

Keeping an index current

# Delta Sync on TRIGGERED: start a sync after the source table changes
index = client.get_index(index_name="main.rag.doc_chunks_index")
index.sync()

# Direct Vector Access: your code writes every change
live = client.get_index(index_name="main.rag.live_notes_index")
live.upsert([
    {"note_id": 1, "note_text": "Refunds post in 5 days",
     "region": "US", "note_vector": [0.12] * 1024},
])
live.delete(primary_keys=[1])

A CONTINUOUS Delta Sync index updates itself, so sync() is for TRIGGERED pipelines, typically the last task of the job refreshing the chunk table. upsert() takes a list of dicts matching the declared schema; delete() takes a list of primary keys. On a Delta Sync index you change the source table instead.

The index schema is fixed at creation: returning a new source column means building a new index and switching traffic to it.

Every similarity_search argument

ArgumentWhat it does
columnsRequired list of fields to return. Each must be an indexed column.
query_textText query. Embedded for you on a managed-embedding index; also the keyword side of HYBRID.
query_vectorA list of floats, for self-managed and Direct Vector Access indexes.
num_resultsRows to return, default 5. Above 1,000 the SDK pages for you, up to 10,000 in all.
filtersDict on Standard, string on Storage Optimized (next section). Without a query, a filter-only lookup.
query_type"ANN" (default), "HYBRID", or "FULL_TEXT" (Beta, keywords only).
score_thresholdDrops results scoring below the value. With a reranker it is applied before reranking.
rerankerA DatabricksReranker that re-scores the first-pass results.
debug_levelSet to 1 or more to get timings, including reranker time, in debug_info.

Sourced from the Databricks documentation pages "Create AI Search endpoints and indexes" and "Query an AI Search index", and the similarity_search reference in the databricks-ai-search Python SDK (version 0.82).

Filter syntax on each endpoint type

Standard endpoints take a dict whose key holds column and operator; Storage Optimized endpoints take one SQL-like WHERE string.

IntentStandard (dict)Storage Optimized (string)
Equals{"region": "US"}"region = 'US'"
Any of{"id": [200, 300]}"id IN (200, 300)"
Not equal{"color NOT": "red"}"color != 'red'"
Range{"price <": 200}, also <= > >="price < 200"
Either column{"color1 OR color2": ["red", "blue"]}"color1 = 'red' OR color2 = 'blue'"
Text match{"col LIKE": "apple"} matches whole words only"col LIKE '%apple%'" with SQL wildcards
Struct, map{"profile.age": 18}"attrs['voltage'] = 9"
TimestampStorage Optimized only"date > TO_TIMESTAMP('1995-01-01')"

Operators and examples from the "Use filters on queries" section of the Databricks page "Query an AI Search index".

On Standard, several keys in one dict must all match, as in {"region": ["US", "CA"], "doc_year >=": 2024}. A Standard LIKE matches whitespace-separated tokens, so "apple" matches "apple pear" but not "pineapple"; the Storage Optimized LIKE is the SQL one.

The bigger difference is when the filter runs. Storage Optimized endpoints overfetch and then filter what was fetched, so a selective filter can return fewer rows than num_results, or none, even when matching rows exist lower down.

A selective filter on a Storage Optimized endpoint With num_results set to 3 and the filter region equals US, the endpoint first fetches its top six candidates by score: EU, EU, US, APAC, EU, EU. The filter keeps only the one US row, rank 3, so one result comes back. Matching US rows at ranks 9 and 14 were never fetched. num_results=3, filters="region = 'US'" OVERFETCHED BY SCORE #1EU #2EU #3US #4APAC #5EU #6EU AFTER THE FILTER #3US 1 row, not 3 US rows ranked #9 and #14 exist but were never fetched.
Storage Optimized endpoints filter the overfetched candidates, so a narrow filter can come back short.

ANN, HYBRID and the reranker

from databricks.ai_search.reranker import DatabricksReranker

results = index.similarity_search(
    query_text="error E-4031 at checkout",
    columns=["chunk_id", "chunk_text", "source_url"],
    filters={"region": "US"},
    num_results=5,
    query_type="HYBRID",
    reranker=DatabricksReranker(columns_to_rerank=["chunk_text"]),
    debug_level=1,
)
rows = results["result"]["data_array"]
print(results["debug_info"])

ANN compares embeddings only, which suits paraphrased questions. HYBRID runs ANN and a keyword search in parallel and merges them with Reciprocal Rank Fusion, which catches exact codes like the one above; it returns at most 200 results. The reranker is a cross-encoder second pass over the first-pass results. columns_to_rerank is read in order and only its first 2,000 characters count, so put short, informative columns first. num_results is still the final count.

Latency: the docs put the reranker at roughly a 10% quality gain for typically under a second more per query. That is why a latency-critical, high-QPS workload runs plain ANN with reranking off, the sizing call in Chapter 34.

The index as an agent's retriever

from databricks_langchain import VectorSearchRetrieverTool

vs_tool = VectorSearchRetrieverTool(
    index_name="main.rag.doc_chunks_index",
    tool_name="policy_search",
    tool_description="Search refund and shipping policies.",
    num_results=5,
    columns=["chunk_id", "chunk_text", "source_url"],
    filters={"region": "US"},
    query_type="HYBRID",
)
docs = vs_tool.invoke("How long do refunds take?")

For new Python agents the docs recommend VectorSearchRetrieverTool from databricks-langchain (or databricks-openai): the description tells the model when to search, and columns, filters and query_type work as above. On a self-managed or Direct Vector Access index it also needs text_column and an embedding model to embed the query. The DatabricksVectorSearch vector store, wrapped with as_retriever(), still serves a fixed chain, with LangChain's names k and filter.

From the Databricks page "Connect agents to unstructured data" and the databricks-langchain 0.22 package source.

What this means for the exam

Exam tip: a field missing from results is fixed in two places: sync the column into the index, then name it in columns. Per-user scoping is a filters value the application sets from the user's identity, not a security feature inside the index. Exact codes and part numbers point to HYBRID; a tight latency budget points to ANN without the reranker.

Distractors change the wrong argument: more num_results for more fields, HYBRID for metadata, or a join back to the source table.

FAQ

What arguments does similarity_search take in Databricks Vector Search?

similarity_search takes the columns to return plus either query_text or query_vector, and optionally num_results (default 5), filters, query_type (ANN by default, or HYBRID), score_threshold, a reranker and debug_level. Only columns that were synced into the index can be returned or filtered on.

How do filters differ between Standard and Storage Optimized Vector Search endpoints?

Standard endpoints take a dictionary such as {"price <": 200}, with the operator in the key. Storage Optimized endpoints take a SQL-like string such as "price < 200" and apply it to overfetched results, so a narrow filter can return fewer rows than requested.

When do I call sync() on a Vector Search index?

Call index.sync() on a Delta Sync index that uses the TRIGGERED pipeline type, after the source table changes. A CONTINUOUS index updates itself, and a Direct Vector Access index is never synced: your code writes to it with upsert() and delete().

Should I use HYBRID search or ANN in Mosaic AI Vector Search?

Use HYBRID when queries contain exact terms such as product codes or names, because it merges vector and keyword results. Use plain ANN, the default, for paraphrased questions or when latency is the tightest constraint.

Back to the chapter

Creating and querying a Vector Search index is Chapter 33 of the Databricks Generative AI Engineer Associate course, in Unit 4, Assembling and Deploying Applications, with exam-style checks on columns and filters.

Open Chapter 33 →