Skip to main content
โšก Calmops

Solr for AI: Vector Search, RAG Pipelines, and Semantic Search 2026

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:

  1. Document Ingestion: Parse, chunk, and embed documents
  2. Vector Indexing: Store embeddings in Solr
  3. Query Processing: Embed user queries and search for similar content
  4. Context Augmentation: Combine retrieved documents with LLM prompts
  5. 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

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

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

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

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


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:

  1. Open Source Flexibility - Full control over your search infrastructure
  2. Hybrid Search - Combine keyword and vector search out of the box
  3. Existing Infrastructure - Leverage your current Solr deployment
  4. Enterprise Features - Security, monitoring, and scalability
  5. 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