Skip to content

romiluz13/Hybrid-Search-RAG

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

86 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
β–ˆβ–ˆβ•—  β–ˆβ–ˆβ•—β–ˆβ–ˆβ•—   β–ˆβ–ˆβ•—β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•— β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•— β–ˆβ–ˆβ•—β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•— β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—  β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—  β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—
β–ˆβ–ˆβ•‘  β–ˆβ–ˆβ•‘β•šβ–ˆβ–ˆβ•— β–ˆβ–ˆβ•”β•β–ˆβ–ˆβ•”β•β•β–ˆβ–ˆβ•—β–ˆβ–ˆβ•”β•β•β–ˆβ–ˆβ•—β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•”β•β•β–ˆβ–ˆβ•—β–ˆβ–ˆβ•”β•β•β–ˆβ–ˆβ•—β–ˆβ–ˆβ•”β•β•β–ˆβ–ˆβ•—β–ˆβ–ˆβ•”β•β•β•β•β•
β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•‘ β•šβ–ˆβ–ˆβ–ˆβ–ˆβ•”β• β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•”β•β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•”β•β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘  β–ˆβ–ˆβ•‘β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•”β•β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘  β–ˆβ–ˆβ–ˆβ•—
β–ˆβ–ˆβ•”β•β•β–ˆβ–ˆβ•‘  β•šβ–ˆβ–ˆβ•”β•  β–ˆβ–ˆβ•”β•β•β–ˆβ–ˆβ•—β–ˆβ–ˆβ•”β•β•β–ˆβ–ˆβ•—β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘  β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•”β•β•β–ˆβ–ˆβ•—β–ˆβ–ˆβ•”β•β•β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘   β–ˆβ–ˆβ•‘
β–ˆβ–ˆβ•‘  β–ˆβ–ˆβ•‘   β–ˆβ–ˆβ•‘   β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•”β•β–ˆβ–ˆβ•‘  β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•”β•β–ˆβ–ˆβ•‘  β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘  β–ˆβ–ˆβ•‘β•šβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•”β•
β•šβ•β•  β•šβ•β•   β•šβ•β•   β•šβ•β•β•β•β•β• β•šβ•β•  β•šβ•β•β•šβ•β•β•šβ•β•β•β•β•β• β•šβ•β•  β•šβ•β•β•šβ•β•  β•šβ•β• β•šβ•β•β•β•β•β•

MongoDB-Native AI Starter

Backend-First β€’ MongoDB 8.2 Native β€’ Hybrid Search β€’ Knowledge Graph

HybridRAG is a backend-first Python starter for MongoDB-native AI applications with real live validation on a blessed stack.

License Python MongoDB Voyage AI PRs Welcome

Features β€’ MongoDB 8.2 β€’ Quick Start β€’ Documentation β€’ Contributing


🎯 The Problem

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  THE FRAGMENTED WAY                                                      β”‚
β”‚                                                                          β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                 β”‚
β”‚  β”‚ Pinecone β”‚  β”‚  Neo4j   β”‚  β”‚  Redis   β”‚  β”‚ Postgres β”‚                 β”‚
β”‚  β”‚ Vectors  β”‚  β”‚  Graph   β”‚  β”‚  Cache   β”‚  β”‚ Metadata β”‚                 β”‚
β”‚  β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜                 β”‚
β”‚       β”‚             β”‚             β”‚             β”‚                        β”‚
β”‚       β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                        β”‚
β”‚                         β”‚                                                β”‚
β”‚                    SYNC HELL 😱                                          β”‚
β”‚         If one write fails, your RAG returns                             β”‚
β”‚         vectors for deleted text                                         β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

                              VS

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  THE HYBRIDRAG WAY                                                       β”‚
β”‚                                                                          β”‚
β”‚                    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                               β”‚
β”‚                    β”‚   MongoDB Atlas     β”‚                               β”‚
β”‚                    β”‚  β”Œβ”€β”€β”€β” β”Œβ”€β”€β”€β” β”Œβ”€β”€β”€β”  β”‚                               β”‚
β”‚                    β”‚  β”‚ V β”‚ β”‚ G β”‚ β”‚ K β”‚  β”‚                               β”‚
β”‚                    β”‚  β””β”€β”€β”€β”˜ β””β”€β”€β”€β”˜ β””β”€β”€β”€β”˜  β”‚                               β”‚
β”‚                    β”‚  Vector Graph  KV   β”‚                               β”‚
β”‚                    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                               β”‚
β”‚                              β”‚                                           β”‚
β”‚                    ONE DOCUMENT = ATOMIC βœ…                              β”‚
β”‚              All or nothing. Never inconsistent.                         β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

✨ Features

πŸ”„ Core Capabilities

Feature Description
Atomic Updates Vector + metadata + graph in one transaction
$rankFusion Native MongoDB 8.0+ weighted hybrid search (RRF)
$scoreFusion Score-based fusion with normalization
Knowledge Graph Automatic entity & relationship extraction
Self-Compacting Memory Conversations auto-summarize

πŸš€ MongoDB 8.2 Native

Feature Description
Lexical Prefilters Fuzzy, phrase, wildcard BEFORE vectors
Dynamic numCandidates Auto-tuned (top_k Γ— 20)
scoreDetails Per-pipeline score debugging
Explicit Weights Configurable vector/text weights
Fail-Fast Support Explicit capability errors on the blessed stack

πŸ”Œ Blessed Stack

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  DATABASE         β”‚  EMBEDDINGS       β”‚  LLM             β”‚  SURFACE      β”‚
β”‚  ────────────     β”‚  ─────────────    β”‚  ────────────    β”‚  ──           β”‚
β”‚  βœ“ MongoDB 8.2+   β”‚  βœ“ Voyage AI      β”‚  βœ“ OpenAI API    β”‚  βœ“ Python API β”‚
β”‚  βœ“ atlas-local    β”‚  βœ“ voyage-4-large β”‚  βœ“ Compatible    β”‚  βœ“ REST API   β”‚
β”‚  βœ“ Atlas cloud    β”‚  βœ“ Reranking      β”‚    endpoints     β”‚  βœ“ Rich CLI   β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

One Supported Story

HybridRAG supports multiple integrations internally, but the public reference path is intentionally narrow:

  • MongoDB: Atlas 8.2+ or mongodb/mongodb-atlas-local:preview on mongodb://localhost:27018/?directConnection=true
  • Embeddings/Rerank: Voyage
  • LLM: OpenAI API or an OpenAI-compatible endpoint
  • Product shape: Python library + FastAPI + CLI

That is the stack exercised by the release gate. Unsupported capabilities on this path should fail explicitly rather than silently degrading.


πŸ†• MongoDB 8.2 Features

HybridRAG is built for MongoDB 8.2 with native support for the latest search operators.

Three Filter Systems

from hybridrag import (
    # 1. Vector Search Filters (MQL - for $vectorSearch)
    VectorSearchFilterConfig,
    build_vector_search_filters,

    # 2. Atlas Search Filters (for $search compound queries)
    AtlasSearchFilterConfig,
    build_atlas_search_filters,

    # 3. Lexical Prefilters (NEW - for $search.vectorSearch)
    LexicalPrefilterConfig,
    build_lexical_prefilters,
    TextFilter, FuzzyFilter, PhraseFilter, WildcardFilter, GeoFilter,
)

Lexical Prefilters (MongoDB 8.2+)

The game-changer: Apply Atlas Search operators (fuzzy, phrase, wildcard, geo) BEFORE vector search.

from hybridrag import LexicalPrefilterConfig, HybridRAG

# Create a lexical prefilter config
filter_config = LexicalPrefilterConfig(
    # Fuzzy text matching (typo-tolerant)
    fuzzy_filters=[{"path": "content", "query": "machin lerning", "maxEdits": 2}],

    # Exact phrase matching
    phrase_filters=[{"path": "title", "query": "vector database"}],

    # Wildcard patterns
    wildcard_filters=[{"path": "tags", "query": "tech*"}],

    # Date range filtering
    range_filters={"timestamp": {"gte": "2024-01-01"}},

    # Geospatial (find docs near a location)
    geo_filters=[{"path": "location", "geometry": {"type": "Point", "coordinates": [-73.9, 40.7]}}],
)

# Use with hybrid search
results = await rag.query(
    query="machine learning best practices",
    mode="hybrid",
    lexical_filter_config=filter_config,
)

Why Lexical Prefilters Matter

Scenario Legacy $vectorSearch New $search.vectorSearch
"Find docs about machin lerning" ❌ No fuzzy support βœ… fuzzy: {maxEdits: 2}
"Exact phrase 'machine learning'" ❌ Vector similarity only βœ… phrase: {slop: 0}
"Tags matching tech*" ❌ No wildcards βœ… wildcard: {query: "tech*"}
"Docs within 10km of NYC" ❌ No geo filtering βœ… geoWithin
"Combined filters" ❌ MQL only ($eq, $gte) βœ… Full Atlas Search syntax

$meta Score Fields Reference

# $vectorSearch and $search use operator-specific $meta keywords.
# $rankFusion and $scoreFusion both expose the combined score via "score".
OPERATOR_SCORE_FIELDS = {
    "$vectorSearch":        "vectorSearchScore",   # legacy $vectorSearch stage
    "$search.vectorSearch": "searchScore",         # MongoDB 8.2+ $search.vectorSearch
    "$rankFusion":          "score",               # MongoDB 8.0+ (also "scoreDetails")
    "$scoreFusion":         "score",               # MongoDB 8.3+ (also "scoreDetails")
}

πŸ”€ How Hybrid Search Works

  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
  β”‚                    $rankFusion (MongoDB 8.2 Native)                  β”‚
  β”‚                                                                      β”‚
  β”‚   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”          β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”      β”‚
  β”‚   β”‚    VECTOR PIPELINE    β”‚          β”‚    TEXT PIPELINE      β”‚      β”‚
  β”‚   β”‚  $search.vectorSearch β”‚          β”‚  $search.compound     β”‚      β”‚
  β”‚   β”‚                       β”‚          β”‚                       β”‚      β”‚
  β”‚   β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚          β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚      β”‚
  β”‚   β”‚  β”‚ Lexical Prefilterβ”‚  β”‚          β”‚  β”‚  Fuzzy Matching β”‚  β”‚      β”‚
  β”‚   β”‚  β”‚ (fuzzy/phrase/  β”‚  β”‚          β”‚  β”‚                 β”‚  β”‚      β”‚
  β”‚   β”‚  β”‚  wildcard/geo)  β”‚  β”‚          β”‚  β”‚                 β”‚  β”‚      β”‚
  β”‚   β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚          β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚      β”‚
  β”‚   β”‚           ↓           β”‚          β”‚           ↓           β”‚      β”‚
  β”‚   β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚          β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚      β”‚
  β”‚   β”‚  β”‚ Vector Similarityβ”‚  β”‚          β”‚  β”‚  BM25 Scoring   β”‚  β”‚      β”‚
  β”‚   β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚          β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚      β”‚
  β”‚   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜          β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜      β”‚
  β”‚               β”‚                                  β”‚                  β”‚
  β”‚               β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                  β”‚
  β”‚                              ↓                                      β”‚
  β”‚                  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                          β”‚
  β”‚                  β”‚  Weighted Fusion      β”‚                          β”‚
  β”‚                  β”‚  vector: 0.6          β”‚                          β”‚
  β”‚                  β”‚  text:   0.4          β”‚                          β”‚
  β”‚                  β”‚  scoreDetails: true   β”‚                          β”‚
  β”‚                  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                          β”‚
  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
  β”‚              KNOWLEDGE GRAPH ($graphLookup)                          β”‚
  β”‚                                                                      β”‚
  β”‚   Entity Boosting: KG relationships enhance reranking scores         β”‚
  β”‚   Mix Mode: Combines vector + text + graph for comprehensive RAG     β”‚
  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

πŸš€ Quick Start

See MongoDB value in 60 seconds (no API keys, no signup)

Prerequisites: Python 3.11+ and Docker Desktop (running).

git clone https://github.com/romiluz13/Hybrid-Search-RAG.git
cd Hybrid-Search-RAG

# Create a virtual environment and install (one-time)
python3 -m venv .venv && source .venv/bin/activate
pip install -e ".[all]"            # or just run: make first-time-setup

make demo                         # starts local MongoDB + runs the showcase

make demo brings up a local MongoDB (mongodb/mongodb-atlas-local:preview via Docker) and runs the real MongoDB 8.2+ native hybrid-search pipeline against seeded data β€” no Voyage key, no LLM key, no Atlas account. You see:

  • $vectorSearch β€” semantic nearest neighbors (cosine)
  • $search β€” lexical / BM25
  • $rankFusion β€” native hybrid search merging vector + lexical via RRF
  • $graphLookup β€” knowledge-graph traversal in one pipeline

The demo uses sample vectors (labeled). In production, embeddings come from Voyage AI β€” that's the make demo-full path below.

Full generative RAG (bring Voyage + LLM keys)

# 1. Configure API keys
cp .env.example .env              # MONGODB_URI defaults to local; add your keys
#    Required: VOYAGE_API_KEY (https://dash.voyageai.com/)
#    Required: one LLM key β€” set LLM_PROVIDER to match:
#              - anthropic (default) β€” ANTHROPIC_API_KEY
#              - openai              β€” OPENAI_API_KEY
#              - gemini              β€” GEMINI_API_KEY
#              - grove               β€” GROVE_API_KEY + GROVE_BASE_URL
#                (MongoDB internal OpenAI-compatible gateway; for SAs
#                 without an OpenAI/Anthropic key)

# 2. Run full RAG (ingests real Voyage embeddings + generates an answer)
make demo-full

# 3. Or start a server
make run-api        # FastAPI  β†’ http://localhost:8000  (/health, /docs)
make run-ui         # Chainlit β†’ http://localhost:8001
make run-cli        # interactive CLI

macOS + Atlas SSL note: if you hit SSL: CERTIFICATE_VERIFY_FAILED against Atlas (common with python.org Python, which doesn't bundle CA certs), HybridRAG now automatically uses certifi's CA bundle for mongodb+srv:// connections. No action needed. For a corporate/custom CA, set MONGODB_TLS_CA_FILE=/path/to/your-ca.pem.

Canonical Query Paths (Python SDK)

# Simple answer
answer = await rag.query("How does hybrid search work?", mode="mix")

# Source-aware response
result = await rag.query_with_sources(
    "How does hybrid search work?",
    mode="mix",
    top_k=5,
)
print(result["answer"])
print(result["references"])

# Streaming response
stream = await rag.stream_query(
    "What should a production RAG boilerplate expose?",
    mode="mix",
    include_context=True,
    include_references=True,
)
async for chunk in stream["response_iterator"]:
    print(chunk, end="")

Release Gate

The repo is considered publish-ready only when the blessed stack passes:

  • fast PR checks
  • API contract tests
  • example smoke tests
  • deterministic live seeded validation on atlas-local:preview
  • no skipped release tests
  • no silent fallback on the blessed path

Basic Usage

import asyncio
from hybridrag import create_hybridrag, LexicalPrefilterConfig

async def main():
    # Initialize
    rag = await create_hybridrag()

    # Ingest documents
    await rag.ingest_files("./documents/")

    # Simple query
    result = await rag.query_with_memory(
        query="What are the key findings?",
        mode="mix",  # Vector + Graph + Keyword
    )
    print(result["answer"])

    # Advanced: Query with lexical prefilters
    filter_config = LexicalPrefilterConfig(
        fuzzy_filters=[{"path": "content", "query": "machin lerning", "maxEdits": 2}],
        range_filters={"timestamp": {"gte": "2024-01-01"}},
    )

    result = await rag.query(
        query="machine learning trends",
        mode="hybrid",
        lexical_filter_config=filter_config,
    )

asyncio.run(main())

CLI

# Launch interactive CLI
hybridrag chat

# Or use Typer commands
hybridrag ingest ./documents/
hybridrag query "What is MongoDB Atlas?"
hybridrag status
hybridrag benchmark

πŸ“Š Query Modes

Mode Description Use Case
mix KG + Vector + Keyword Recommended - General queries
hybrid Vector + Keyword ($rankFusion) Fast hybrid search
local Entity-focused retrieval Specific entities
global Community summaries High-level overview
naive Vector search only Simple similarity

πŸ—οΈ Architecture

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                              HybridRAG                                   β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                                                                          β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚  β”‚   Voyage AI    β”‚  β”‚  Claude/GPT/   β”‚  β”‚      MongoDB Atlas 8.2     β”‚ β”‚
β”‚  β”‚   Embeddings   β”‚  β”‚    Gemini      β”‚  β”‚                            β”‚ β”‚
β”‚  β”‚   + Reranking  β”‚  β”‚                β”‚  β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚ β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚  β”‚ $rankFusion          β”‚  β”‚ β”‚
β”‚                                          β”‚  β”‚ $scoreFusion         β”‚  β”‚ β”‚
β”‚                                          β”‚  β”‚ $search.vectorSearch β”‚  β”‚ β”‚
β”‚                                          β”‚  β”‚ $graphLookup         β”‚  β”‚ β”‚
β”‚                                          β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚ β”‚
β”‚                                          β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”β”‚
β”‚  β”‚                         FILTER SYSTEMS                              β”‚β”‚
β”‚  β”‚  VectorSearchFilterConfig β”‚ AtlasSearchFilterConfig β”‚ LexicalPrefilterβ”‚β”‚
β”‚  β”‚  (MQL: $eq, $gte, $in)    β”‚ (Atlas: range, equals)  β”‚ (fuzzy,phrase) β”‚β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”β”‚
β”‚  β”‚                         ENHANCEMENTS                                 β”‚β”‚
β”‚  β”‚  Entity Boosting β”‚ Query Optimizer β”‚ Self-Compacting Memory         β”‚β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”β”‚
β”‚  β”‚                        INTERFACES                                    β”‚β”‚
β”‚  β”‚        Chainlit UI  β”‚  Typer CLI  β”‚  REST API  β”‚  Python SDK        β”‚β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

πŸ“š Documentation

Document Description
Cookbook 8 production recipes for building AI apps
Installation Guide Setup and configuration
Architecture Decisions ADRs for key decisions
Enhanced Search Graph traversal, mix mode
Notebooks Interactive tutorials (5)
Examples Code examples (8)

Cookbook Recipes

Recipe Topic Description
01 Hybrid Search MongoDB $rankFusion implementation
02 Lexical Prefilters MongoDB 8.2 fuzzy/phrase/wildcard/geo
03 Conversation Memory Multi-turn chat with self-compaction
04 Vector Optimization numCandidates, quantization, tuning
05 Knowledge Graph $graphLookup for entity relationships
06 Filtering Strategies Three filter systems explained
07 Agent Memory Long-term, working, episodic memory
08 Production Scaling, monitoring, security

Architecture Decision Records


πŸ§ͺ Development

# Setup development environment
make first-time-setup

# Run tests
make test              # All tests
make test-quick        # Fast unit tests
make test-cov          # With coverage

# Code quality
make lint              # Ruff linting
make format            # Auto-format
make typecheck         # MyPy

# Full CI suite
make ci

πŸ“Š Why MongoDB Over Postgres?

Task Postgres + pgvector HybridRAG + MongoDB
Add metadata field ALTER TABLE + backfill + reindex Just add it
Change embedding model Rewrite entire table (MVCC bloat) Bulk update, no rewrite
Hybrid search Manual result merging in app code Single $rankFusion pipeline
Lexical prefilters Not supported $search.vectorSearch native
Filter vectors by metadata Separate index, query planner struggles Compound index, native
Time to first query Hours (extensions, schema, indexes) 30 minutes (Atlas free tier)

🀝 Contributing

We welcome contributions! See CONTRIBUTING.md for guidelines.

# Development setup
git clone https://github.com/romiluz13/Hybrid-Search-RAG.git
cd Hybrid-Search-RAG
make first-time-setup

# Run tests before submitting
make ci

πŸ“œ License

Apache License 2.0 - see LICENSE for details.


╔═══════════════════════════════════════════════════════════════════════════╗
β•‘                                                                           β•‘
β•‘   MongoDB 8.0+ Native: $rankFusion β€’ $scoreFusion(8.3+) β€’ $search.vectorSearch   β•‘
β•‘                                                                           β•‘
β•‘   Three Filter Systems: Vector (MQL) β€’ Atlas β€’ Lexical Prefilters         β•‘
β•‘                                                                           β•‘
β•‘   One MongoDB document. Atomic updates. Never inconsistent.               β•‘
β•‘                                                                           β•‘
β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•

Built with MongoDB 8.2 β€’ Voyage AI β€’ Claude

⬆ Back to Top

About

No description, website, or topics provided.

Resources

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages