Introduction
Apache Solr has evolved from a traditional search engine into a powerful platform for AI applications. With native vector search support, hybrid search capabilities, and seamless integration with LLMs, Solr enables developers to build sophisticated semantic search and retrieval-augmented generation (RAG) pipelines. In 2026, Solr continues to gain traction as organizations seek open-source alternatives to proprietary vector databases while leveraging their existing Solr infrastructure.
This comprehensive guide covers everything you need to know about using Solr for AI applications, from basic vector search setup to advanced RAG implementations and production best practices.
Understanding Solr’s Vector Search Capabilities
What is Vector Search in Solr?
Vector search in Solr enables similarity search over high-dimensional embeddings generated by machine learning models. Unlike traditional keyword search that matches exact terms, vector search finds semantically similar content by comparing numerical representations of meaning.
Key features of Solr’s vector search:
- KNN (K-Nearest Neighbors) search: Find the most similar vectors to a query vector
- Hybrid search: Combine keyword search with vector similarity for balanced results
- Multiple distance metrics: Cosine similarity, Euclidean distance, inner product
- Scalable indexing: HNSW (Hierarchical Navigable Small World) graphs for fast ANN search
Supported Vector Field Types
Solr supports several vector field types optimized for different use cases:
{
"name": "knn_vector",
"type": "knn_vector",
"dimension": 768,
"method": {
"name": "hnsw",
"type": "hnsw",
"m": 16,
"ef_construction": 100,
"similarity": "cosine"
}
}
The key parameters:
- dimension: Number of dimensions in your embedding vectors (1536 for OpenAI ada-002, 768 for many transformer models)
- m: Number of connections per node in HNSW graph (16 is a good default)
- ef_construction: Size of the dynamic candidate list during index build (100-1000)
- similarity: Distance metric (cosine, euclidean, inner_product)
Vector Distance Metrics
Solr supports multiple distance metrics for vector similarity:
Cosine Similarity - Measures the angle between vectors:
{
"similarity": "cosine",
"description": "Best for semantic similarity, insensitive to vector magnitude"
}
Euclidean Distance - Measures straight-line distance:
{
"similarity": "euclidean",
"description": "Best when vector magnitude matters, sensitive to scale"
}
Inner Product - Measures dot product:
{
"similarity": "inner_product",
"description": "Equivalent to cosine for normalized vectors, faster computation"
}
Setting Up Vector Search in Solr
Step 1: Configure the Schema
Create a schema with vector fields and supporting fields:
{
"add-field-type": {
"name": "knn_vector",
"class": "solr.KnnVectorField",
"dimension": 1536,
"index": true,
"stored": false,
"vectorSimilarityFunction": "cosine"
},
"add-field": {
"name": "content_embedding",
"type": "knn_vector",
"dimension": 1536,
"indexed": true,
"stored": false
},
"add-field": {
"name": "title_embedding",
"type": "knn_vector",
"dimension": 1536,
"indexed": true,
"stored": false
}
}
Step 2: Create a Search Component
Configure vector search components in solrconfig.xml:
<searchComponent name="knnSearch" class="solr.KnnVectorSearchComponent">
<int name="maxResults">100</int>
<int name="minMatches">10</int>
</searchComponent>
<requestHandler name="/select" class="solr.SearchHandler">
<lst name="defaults">
<str name="defType">edismax</str>
<str name="qf">title^2.0 content^1.0</str>
</lst>
<arr name="components">
<str>knnSearch</str>
<str>query</str>
<str>facet</str>
<str>debug</str>
</arr>
</requestHandler>
Step 3: Index Documents with Vectors
Index documents with both text and vector fields:
{
"add": {
"doc": {
"id": "doc-1",
"title": "Introduction to Machine Learning",
"content": "Machine learning is a subset of artificial intelligence...",
"content_embedding": [0.1, 0.2, 0.3, ..., 0.9],
"title_embedding": [0.2, 0.3, 0.4, ..., 0.8],
"created_at": "2026-03-05T10:00:00Z"
}
}
}
Building RAG Pipelines with Solr
RAG Architecture Overview
Retrieval-Augmented Generation (RAG) combines vector search with LLMs to provide context-aware responses. The architecture consists of:
- Document Ingestion: Parse, chunk, and embed documents
- Vector Indexing: Store embeddings in Solr
- Query Processing: Embed user queries and search for similar content
- Context Augmentation: Combine retrieved documents with LLM prompts
- Response Generation: Generate answers using LLM with context
Document Ingestion Pipeline
import pysolr
import numpy as np
from typing import List, Dict, Optional
import json
class SolrRAGIngestion:
def __init__(self, solr_url: str, embedding_model):
self.solr = pysolr.Solr(solr_url)
self.model = embedding_model
self.chunk_size = 1000
self.chunk_overlap = 200
def chunk_text(self, text: str) -> List[str]:
"""Split text into chunks with overlap"""
chunks = []
words = text.split()
for i in range(0, len(words), self.chunk_size - self.chunk_overlap):
chunk = ' '.join(words[i:i + self.chunk_size])
if chunk:
chunks.append(chunk)
return chunks
def embed_and_index(self, document: Dict) -> List[str]:
"""Embed and index a document"""
doc_ids = []
# Create chunks
chunks = self.chunk_text(document['content'])
for i, chunk in enumerate(chunks):
# Generate embedding
embedding = self.model.encode(chunk).tolist()
# Create document with chunk metadata
chunk_doc = {
'id': f"{document['id']}_chunk_{i}",
'parent_id': document['id'],
'chunk_index': i,
'content': chunk,
'content_embedding': embedding,
'title': document.get('title', ''),
'created_at': document.get('created_at', ''),
'source': document.get('source', '')
}
doc_ids.append(chunk_doc['id'])
# Index all chunks
self.solr.add([chunk_doc for chunk_doc in doc_ids])
return doc_ids
def batch_ingest(self, documents: List[Dict]) -> int:
"""Batch ingest multiple documents"""
total_chunks = 0
all_docs = []
for doc in documents:
chunks = self.chunk_text(doc['content'])
for i, chunk in enumerate(chunks):
embedding = self.model.encode(chunk).tolist()
all_docs.append({
'id': f"{doc['id']}_chunk_{i}",
'parent_id': doc['id'],
'chunk_index': i,
'content': chunk,
'content_embedding': embedding,
'title': doc.get('title', ''),
'created_at': doc.get('created_at', ''),
'source': doc.get('source', '')
})
total_chunks += 1
self.solr.add(all_docs)
return total_chunks
Query Processing and Retrieval
class SolrRAGQuery:
def __init__(self, solr_url: str, embedding_model, k: int = 5):
self.solr = pysolr.Solr(solr_url)
self.model = embedding_model
self.k = k
def retrieve(self, query: str, filters: Dict = None) -> List[Dict]:
"""Retrieve relevant documents for a query"""
# Generate query embedding
query_embedding = self.model.encode(query).tolist()
# Build vector search query
vector_query = f"{{!knn f=content_embedding topK={self.k}}}[{','.join(map(str, query_embedding))}]"
# Build filter query if provided
fq = []
if filters:
if filters.get('source'):
fq.append(f"source:{filters['source']}")
if filters.get('date_from'):
fq.append(f"created_at:[{filters['date_from']} TO *]")
# Execute search
results = self.solr.search(
q=vector_query,
fq=fq,
fl='id,parent_id,content,score',
sort='score desc'
)
return [
{
'id': doc['id'],
'parent_id': doc.get('parent_id'),
'content': doc['content'],
'score': doc['score'],
'relevance': float(doc['score'])
}
for doc in results
]
def retrieve_with_metadata(self, query: str,
metadata_fields: List[str] = None) -> List[Dict]:
"""Retrieve documents with additional metadata"""
query_embedding = self.model.encode(query).tolist()
fields = ['id', 'parent_id', 'content', 'score'] + (metadata_fields or [])
results = self.solr.search(
q=f"{{!knn f=content_embedding topK={self.k}}}[{','.join(map(str, query_embedding))}]",
fl=','.join(fields)
)
return [
{field: doc.get(field) for field in fields}
for doc in results
]
Hybrid Search Implementation
Combine keyword search with vector search for better results:
class HybridSearchRAG:
def __init__(self, solr_url: str, embedding_model, k: int = 5):
self.solr = pysolr.Solr(solr_url)
self.model = embedding_model
self.k = k
def hybrid_search(self, query: str,
keyword_boost: float = 2.0,
vector_boost: float = 1.0) -> List[Dict]:
"""Combine keyword and vector search results"""
query_embedding = self.model.encode(query).tolist()
# Vector search component
vector_q = f"{{!knn f=content_embedding topK={self.k}}}[{','.join(map(str, query_embedding))}]"
# Keyword search component
keyword_q = f"content:{query}"
# Combined query using dismax
combined_q = f"({vector_q}^{vector_boost}) | ({keyword_q}^{keyword_boost})"
results = self.solr.search(
q=combined_q,
qf='content^1.0',
defType='edismax',
fl='id,parent_id,content,score'
)
return [
{
'id': doc['id'],
'content': doc['content'],
'score': doc['score'],
'retrieval_method': 'hybrid'
}
for doc in results
]
Advanced Solr AI Features
Multi-Vector Search
Support multiple embedding types for richer search:
{
"add-field": {
"name": "title_embedding",
"type": "knn_vector",
"dimension": 1536
},
"add-field": {
"name": "content_embedding",
"type": "knn_vector",
"dimension": 1536
},
"add-field": {
"name": "metadata_embedding",
"type": "knn_vector",
"dimension": 1536
}
}
Query multiple vectors:
def multi_vector_search(self, query: str,
weights: Dict[str, float] = None) -> List[Dict]:
"""Search across multiple vector fields"""
query_embedding = self.model.encode(query).tolist()
weights = weights or {
'title_embedding': 2.0,
'content_embedding': 1.0,
'metadata_embedding': 0.5
}
vector_queries = []
for field, weight in weights.items():
vector_queries.append(
f"{{!knn f={field} topK={self.k}}}[{','.join(map(str, query_embedding))}]^{weight}"
)
combined_q = ' | '.join(vector_queries)
results = self.solr.search(q=combined_q, fl='id,content,score')
return results
Filtered Vector Search
Apply metadata filters to vector search:
def filtered_vector_search(self, query: str,
filters: Dict,
top_k: int = 10) -> List[Dict]:
"""Vector search with metadata filters"""
query_embedding = self.model.encode(query).tolist()
# Build filter query
fq = []
for field, value in filters.items():
if isinstance(value, list):
fq.append(f"{field}:({' OR '.join(value)})")
else:
fq.append(f"{field}:{value}")
vector_query = f"{{!knn f=content_embedding topK={top_k}}}[{','.join(map(str, query_embedding))}]"
results = self.solr.search(
q=vector_query,
fq=fq,
fl='id,content,score'
)
return results
Range-based Vector Search
Combine vector search with range queries:
def range_vector_search(self, query: str,
date_from: str = None,
date_to: str = None,
score_min: float = 0.5) -> List[Dict]:
"""Vector search with date and score ranges"""
query_embedding = self.model.encode(query).tolist()
fq = []
if date_from:
fq.append(f"created_at:[{date_from} TO *]")
if date_to:
fq.append(f"created_at:[* TO {date_to}]")
vector_query = f"{{!knn f=content_embedding topK=10}}[{','.join(map(str, query_embedding))}]"
results = self.solr.search(
q=vector_query,
fq=fq,
fl='id,content,score'
)
# Filter by minimum score
return [r for r in results if r['score'] >= score_min]
Production Best Practices
Index Optimization
Optimize HNSW parameters for your use case:
{
"name": "optimized_vector",
"type": "knn_vector",
"dimension": 1536,
"method": {
"name": "hnsw",
"type": "hnsw",
"m": 24,
"ef_construction": 500,
"similarity": "cosine"
},
"docValues": true,
"stored": false
}
Parameters to tune:
- m: Higher values (24-48) improve accuracy but increase index size
- ef_construction: Higher values (500-1000) improve index quality
- docValues: Enable for sorting and faceting
Memory and Performance Tuning
Configure Solr for vector search performance:
# JVM options for vector search
SOLR_JAVA_MEM="-Xms8g -Xmx8g"
# Vector search specific settings
-Dsolr.knn.vector.memory.max.bytes=2147483648
-Dsolr.knn.vector.index.max.docs=1000000
Monitoring and Metrics
Track vector search performance:
{
"get": {
"handler": "/admin/metrics",
"params": {
"cat": "SEARCHER",
"group": "knn"
}
}
}
Key metrics to monitor:
- query_time: Average query latency
- requests: Query throughput
- errors: Query error rate
- cache_hit_ratio: Vector cache effectiveness
Security Considerations
Secure vector search endpoints:
<!-- solrconfig.xml security settings -->
<requestHandler name="/select" class="solr.SearchHandler">
<lst name="defaults">
<str name="defType">edismax</str>
</lst>
<lst name="access">
<str name="user">search_user</str>
<str name="password">{xor}encrypted_password</str>
</lst>
</requestHandler>
Use Cases and Examples
E-commerce Product Search
Build semantic product search with Solr:
class EcommerceProductSearch:
def __init__(self, solr_url, embedding_model):
self.solr = pysolr.Solr(solr_url)
self.model = embedding_model
def search_products(self, query: str,
category: str = None,
min_price: float = 0,
max_price: float = 1000) -> List[Dict]:
"""Search products with semantic understanding"""
query_embedding = self.model.encode(query).tolist()
fq = [f"price:[{min_price} TO {max_price}]"]
if category:
fq.append(f"category:{category}")
vector_query = f"{{!knn f=product_embedding topK=20}}[{','.join(map(str, query_embedding))}]"
results = self.solr.search(
q=vector_query,
fq=fq,
fl='id,name,price,category,score'
)
return results
Content Recommendation System
Build content recommendations:
class ContentRecommendation:
def __init__(self, solr_url, embedding_model):
self.solr = pysolr.Solr(solr_url)
self.model = embedding_model
def get_similar_content(self, content_id: str,
exclude_id: str = None,
limit: int = 5) -> List[Dict]:
"""Find similar content to a given document"""
# Get the original document's embedding
original = self.solr.search(f"id:{content_id}")
if not original:
return []
embedding = original[0]['content_embedding']
fq = []
if exclude_id:
fq.append(f"-id:{exclude_id}")
vector_query = f"{{!knn f=content_embedding topK={limit}}}[{','.join(map(str, embedding))}]"
results = self.solr.search(
q=vector_query,
fq=fq,
fl='id,title,score'
)
return results
Comparison with Alternatives
Solr vs Dedicated Vector Databases
| Feature | Solr | Pinecone | Weaviate | PostgreSQL pgvector |
|---|---|---|---|---|
| Open Source | Yes | No | Yes | Yes |
| Hybrid Search | Native | Limited | Native | Requires custom |
| Existing Infrastructure | Leverage current | New | New | New |
| Scalability | Horizontal | Managed | Horizontal | Vertical + read replicas |
| Cost | Self-managed | Pay-per-use | Pay-per-use | Infrastructure cost |
| Learning Curve | Moderate | Low | Moderate | Low |
When to Choose Solr for AI
Choose Solr when:
- You already have Solr infrastructure
- You need hybrid search (keyword + vector)
- You want open-source control
- You need enterprise features (security, monitoring)
- You have mixed workloads (search + AI)
Consider alternatives when:
- You need pure vector search at massive scale
- You prefer managed services
- You need specialized vector algorithms
- You want simpler setup
Resources
Official Documentation
Community Resources
Tools and Libraries
- PySolr - Python client
- SolrJ - Java client
- Solr Admin UI
Conclusion
Apache Solr has matured into a capable platform for AI applications, offering native vector search, hybrid search capabilities, and seamless integration with LLMs. The key advantages for AI workloads include:
- Open Source Flexibility - Full control over your search infrastructure
- Hybrid Search - Combine keyword and vector search out of the box
- Existing Infrastructure - Leverage your current Solr deployment
- Enterprise Features - Security, monitoring, and scalability
- Cost Efficiency - No per-query pricing or vendor lock-in
By following the patterns and best practices outlined in this guide, you can build production-ready AI applications with Solr, from semantic search engines to RAG pipelines and recommendation systems. The integration with modern embedding models and LLMs makes Solr a compelling choice for organizations seeking open-source AI solutions.
As vector search continues to evolve, Solr’s active development community and enterprise adoption ensure it will remain a viable option for AI applications in 2026 and beyond.
Comments