6 min read
Vector Databases on Azure: Comparing Options for AI Applications
Vector databases are essential for AI applications, particularly RAG (Retrieval Augmented Generation). Azure offers several options, each with different strengths. Let’s compare them to help you choose.
Vector Database Options on Azure
Azure AI Search
├── Full-text + Vector hybrid search
├── Semantic ranking
├── Built-in Azure integration
└── Managed service
Azure Cosmos DB (NoSQL + Vector)
├── Global distribution
├── Multi-model database
├── Integrated vector search
└── Transactional + analytical
PostgreSQL + pgvector
├── Familiar SQL interface
├── Flexible schema
├── Open source
└── Azure Database for PostgreSQL
Third-party (on Azure)
├── Pinecone
├── Weaviate
├── Qdrant
└── Milvus
Azure AI Search
Best for: Enterprise search, RAG applications with complex filtering
from azure.search.documents import SearchClient
from azure.search.documents.indexes import SearchIndexClient
from azure.search.documents.indexes.models import (
SearchIndex,
SearchField,
SearchFieldDataType,
VectorSearch,
HnswAlgorithmConfiguration,
VectorSearchProfile
)
from azure.identity import DefaultAzureCredential
# Create index with vector search
index_client = SearchIndexClient(
endpoint="https://my-search.search.windows.net",
credential=DefaultAzureCredential()
)
index = SearchIndex(
name="documents",
fields=[
SearchField(name="id", type=SearchFieldDataType.String, key=True),
SearchField(name="title", type=SearchFieldDataType.String, searchable=True),
SearchField(name="content", type=SearchFieldDataType.String, searchable=True),
SearchField(name="category", type=SearchFieldDataType.String, filterable=True, facetable=True),
SearchField(
name="content_vector",
type=SearchFieldDataType.Collection(SearchFieldDataType.Single),
searchable=True,
vector_search_dimensions=1536,
vector_search_profile_name="my-vector-config"
)
],
vector_search=VectorSearch(
algorithms=[HnswAlgorithmConfiguration(name="my-hnsw")],
profiles=[VectorSearchProfile(name="my-vector-config", algorithm_configuration_name="my-hnsw")]
)
)
index_client.create_or_update_index(index)
# Search with hybrid query
search_client = SearchClient(
endpoint="https://my-search.search.windows.net",
index_name="documents",
credential=DefaultAzureCredential()
)
query_vector = embedding_model.embed("search query")
results = search_client.search(
search_text="search query", # Full-text search
vector_queries=[{
"vector": query_vector,
"k_nearest_neighbors": 10,
"fields": "content_vector"
}],
filter="category eq 'technical'",
query_type="semantic",
semantic_configuration_name="my-semantic-config",
top=10
)
Pros:
- Hybrid search (vector + keyword + semantic)
- Built-in semantic ranker
- Great Azure integration
- Enterprise security features
Cons:
- Can be expensive at scale
- Limited vector search customization
- Complex pricing model
Azure Cosmos DB
Best for: Global applications, multi-model data, transactional + vector workloads
from azure.cosmos import CosmosClient
from azure.identity import DefaultAzureCredential
# Connect to Cosmos DB with vector search enabled
client = CosmosClient(
url="https://my-cosmos.documents.azure.com:443/",
credential=DefaultAzureCredential()
)
database = client.get_database_client("vectordb")
container = database.get_container_client("documents")
# Container must be created with vector indexing policy
# Example container creation with vector index:
# container_properties = {
# "id": "documents",
# "partitionKey": {"paths": ["/category"]},
# "vectorEmbeddingPolicy": {
# "vectorEmbeddings": [{
# "path": "/embedding",
# "dataType": "float32",
# "dimensions": 1536,
# "distanceFunction": "cosine"
# }]
# },
# "indexingPolicy": {
# "vectorIndexes": [{
# "path": "/embedding",
# "type": "quantizedFlat"
# }]
# }
# }
# Insert document with vector
doc = {
"id": "doc1",
"title": "Sample Document",
"content": "This is the content...",
"category": "technical",
"embedding": embedding_model.embed("This is the content...")
}
container.upsert_item(doc)
# Vector search query
query_vector = embedding_model.embed("search query")
query = """
SELECT TOP 10 c.id, c.title, c.content,
VectorDistance(c.embedding, @queryVector) AS score
FROM c
WHERE c.category = @category
ORDER BY VectorDistance(c.embedding, @queryVector)
"""
results = container.query_items(
query=query,
parameters=[
{"name": "@queryVector", "value": query_vector},
{"name": "@category", "value": "technical"}
]
)
Pros:
- Global distribution
- Transactional guarantees
- Multi-model (document, graph, vector)
- Integrated with existing Cosmos workloads
Cons:
- Vector search is newer feature
- Limited vector search algorithms
- RU-based pricing can be complex
PostgreSQL with pgvector
Best for: Teams familiar with SQL, flexible schemas, cost-effective
import psycopg2
from pgvector.psycopg2 import register_vector
# Connect to Azure Database for PostgreSQL
conn = psycopg2.connect(
host="my-postgres.postgres.database.azure.com",
database="vectordb",
user="admin@my-postgres",
password=os.environ["PG_PASSWORD"],
sslmode="require"
)
register_vector(conn)
# Create table with vector column
with conn.cursor() as cur:
cur.execute("""
CREATE EXTENSION IF NOT EXISTS vector;
CREATE TABLE IF NOT EXISTS documents (
id SERIAL PRIMARY KEY,
title TEXT NOT NULL,
content TEXT NOT NULL,
category TEXT,
embedding vector(1536),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX IF NOT EXISTS documents_embedding_idx
ON documents USING ivfflat (embedding vector_cosine_ops)
WITH (lists = 100);
""")
conn.commit()
# Insert with vector
def insert_document(title, content, category, embedding):
with conn.cursor() as cur:
cur.execute("""
INSERT INTO documents (title, content, category, embedding)
VALUES (%s, %s, %s, %s)
RETURNING id
""", (title, content, category, embedding))
conn.commit()
return cur.fetchone()[0]
# Vector search
def search_similar(query_vector, category=None, limit=10):
with conn.cursor() as cur:
if category:
cur.execute("""
SELECT id, title, content, 1 - (embedding <=> %s) AS similarity
FROM documents
WHERE category = %s
ORDER BY embedding <=> %s
LIMIT %s
""", (query_vector, category, query_vector, limit))
else:
cur.execute("""
SELECT id, title, content, 1 - (embedding <=> %s) AS similarity
FROM documents
ORDER BY embedding <=> %s
LIMIT %s
""", (query_vector, query_vector, limit))
return cur.fetchall()
Pros:
- Familiar SQL interface
- Cost-effective
- Flexible indexing options
- Full PostgreSQL capabilities
Cons:
- Manual scaling required
- Less sophisticated than dedicated vector DBs
- No built-in hybrid search
Comparison Matrix
| Feature | AI Search | Cosmos DB | PostgreSQL |
|---|---|---|---|
| Hybrid Search | Excellent | Basic | Manual |
| Scalability | Auto | Global | Manual |
| Cost | High | Variable | Low |
| Learning Curve | Medium | Medium | Low |
| Vector Algorithms | HNSW | DiskANN | IVFFlat, HNSW |
| Filtering | Excellent | Good | Good |
| Azure Integration | Excellent | Excellent | Good |
Choosing the Right Option
def recommend_vector_db(requirements: dict) -> str:
"""Recommend vector database based on requirements."""
# High-priority requirements
if requirements.get("global_distribution"):
return "cosmos_db"
if requirements.get("advanced_text_search"):
return "ai_search"
if requirements.get("cost_sensitive") and requirements.get("sql_preferred"):
return "postgresql"
# Workload patterns
if requirements.get("read_heavy") and requirements.get("complex_filters"):
return "ai_search"
if requirements.get("mixed_workload"): # Vector + transactional
return "cosmos_db"
if requirements.get("existing_postgres"):
return "postgresql"
# Default recommendation
return "ai_search"
# Example
requirements = {
"global_distribution": False,
"advanced_text_search": True,
"cost_sensitive": False,
"complex_filters": True
}
print(recommend_vector_db(requirements)) # ai_search
Hybrid Architecture
For complex scenarios, combine multiple options:
class HybridVectorStore:
"""Combine AI Search for search with Cosmos for storage."""
def __init__(self, search_client, cosmos_container):
self.search = search_client
self.cosmos = cosmos_container
def index(self, doc_id: str, content: str, embedding: list, metadata: dict):
"""Store in both systems."""
# Full document in Cosmos
self.cosmos.upsert_item({
"id": doc_id,
"content": content,
"embedding": embedding,
**metadata
})
# Searchable index in AI Search
self.search.upload_documents([{
"id": doc_id,
"content": content,
"content_vector": embedding,
**{k: v for k, v in metadata.items() if self._is_searchable(k)}
}])
def search(self, query: str, query_vector: list, **kwargs) -> list:
"""Search via AI Search, enrich from Cosmos."""
# Get IDs from search
results = self.search.search(
search_text=query,
vector_queries=[{"vector": query_vector, "k_nearest_neighbors": 10, "fields": "content_vector"}],
select=["id"],
**kwargs
)
# Enrich with full documents from Cosmos
enriched = []
for result in results:
doc = self.cosmos.read_item(result["id"], partition_key=result["id"])
enriched.append({**doc, "score": result["@search.score"]})
return enriched
Choose based on your specific requirements, team expertise, and existing infrastructure. There’s no one-size-fits-all solution for vector databases.