mirror of
https://github.com/open-webui/open-webui.git
synced 2026-05-06 10:58:17 -05:00
[PR #23275] feat: add BM25 hybrid search support for Qdrant (multitenancy mode) #42748
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
📋 Pull Request Information
Original PR: https://github.com/open-webui/open-webui/pull/23275
Author: @MiXaiLL76
Created: 3/31/2026
Status: 🔄 Open
Base:
dev← Head:qdrant-bm25📝 Commits (10+)
fe6783cMerge pull request #19030 from open-webui/devfc05e0aMerge pull request #19405 from open-webui/deve3faec6Merge pull request #19416 from open-webui/dev9899293Merge pull request #19448 from open-webui/dev140605eMerge pull request #19462 from open-webui/dev6f1486fMerge pull request #19466 from open-webui/devd95f533Merge pull request #19729 from open-webui/deva7271530.6.43 (#20093)6adde20Merge pull request #20394 from open-webui/devf9b0534Merge pull request #20522 from open-webui/dev📊 Changes
22 files changed (+227 additions, -40 deletions)
View changed files
📝
backend/open_webui/config.py(+6 -0)📝
backend/open_webui/retrieval/utils.py(+7 -4)📝
backend/open_webui/retrieval/vector/dbs/chroma.py(+1 -0)📝
backend/open_webui/retrieval/vector/dbs/elasticsearch.py(+1 -0)📝
backend/open_webui/retrieval/vector/dbs/mariadb_vector.py(+1 -0)📝
backend/open_webui/retrieval/vector/dbs/milvus.py(+1 -0)📝
backend/open_webui/retrieval/vector/dbs/milvus_multitenancy.py(+1 -0)📝
backend/open_webui/retrieval/vector/dbs/opengauss.py(+1 -0)📝
backend/open_webui/retrieval/vector/dbs/opensearch.py(+1 -0)📝
backend/open_webui/retrieval/vector/dbs/oracle23ai.py(+1 -0)📝
backend/open_webui/retrieval/vector/dbs/pgvector.py(+1 -0)📝
backend/open_webui/retrieval/vector/dbs/pinecone.py(+1 -0)📝
backend/open_webui/retrieval/vector/dbs/qdrant.py(+1 -0)📝
backend/open_webui/retrieval/vector/dbs/qdrant_multitenancy.py(+195 -36)📝
backend/open_webui/retrieval/vector/dbs/s3vector.py(+1 -0)📝
backend/open_webui/retrieval/vector/dbs/weaviate.py(+1 -0)📝
backend/open_webui/retrieval/vector/main.py(+1 -0)📝
backend/open_webui/routers/memories.py(+1 -0)📝
backend/open_webui/routers/retrieval.py(+1 -0)📝
backend/open_webui/tools/builtin.py(+1 -0)...and 2 more files
📄 Description
Changelog Entry
Description
Adds hybrid search (dense + sparse BM25) support to the Qdrant multitenancy backend using fastembed for sparse text embeddings and Reciprocal Rank Fusion (RRF) for result merging.
This improves retrieval quality by combining semantic (dense vector) similarity with keyword-based (BM25 sparse) relevance, particularly for queries where exact keyword matching matters alongside semantic similarity.
Hybrid search uses built-in qdrant functions and mechanisms.
To enable hybrid search, the raw query text is now propagated through the entire retrieval pipeline — from routers and tools down to the
VectorDBBase.search()interface — via a new optionalquery: strparameter. All other vector DB backends accept this parameter without behavioral change (they simply ignore it), preserving full backward compatibility.Added
fastembedSparseTextEmbedding:FusionQuery(RRF).QDRANT_HYBRID_SEARCH_ENABLED(default:false) — enable/disable hybrid search.QDRANT_SPARSE_EMBEDDING_MODEL(default:Qdrant/bm25) — fastembed sparse model name.QDRANT_DENSE_VECTOR_NAME(default:dense) — named vector key for dense embeddings.QDRANT_SPARSE_VECTOR_NAME(default:sparse) — named vector key for sparse embeddings.QDRANT_HYBRID_SEARCH_FUSION_TYPE(default:rrf) — Fusion typeQDRANT_SPARSE_ON_DISK(default:false) — store sparse index on disk.fastembed==0.8.0(optional — hybrid search is gracefully disabled if not installed).Changed
VectorDBBase.search()signature extended withquery: Optional[str] = Noneacross all backends (Chroma, Elasticsearch, MariaDB, Milvus, Milvus multitenancy, OpenGauss, OpenSearch, Oracle 23ai, pgvector, Pinecone, Qdrant, Qdrant multitenancy, S3Vector, Weaviate).query_doc()andprocess_query_collection()inretrieval/utils.pynow receive and forwardquery_textso the raw query string reaches the vector DB layer.query_collection()inretrieval/utils.pynow zipsquerieswithquery_embeddingswhen dispatching to thread pool workers.routers/retrieval.py—query_doc_handlerpassesform_data.queryasquery_text.routers/memories.py— memory search passesform_data.contentasquery.tools/builtin.py— knowledge-base search passes the rawquerystring.{"dense": VectorParams, ...}) when hybrid mode is active.Deprecated
Removed
Fixed
Security
Breaking Changes
QDRANT_HYBRID_SEARCH_ENABLED=trueuse a named-vector schema ({"dense": ..., "sparse": ...}) instead of the previous unnamed single-vector schema. Existing collections created before this change will continue to work in dense-only mode (detected automatically via_is_hybrid_collection()). To migrate an existing collection to hybrid, it must be re-created (drop and re-index).Additional Information
fastembedis not installed — a warning is logged and dense-only search is used as fallback._is_hybrid_collection()helper inspects the live Qdrant collection config, so mixed environments (some hybrid, some legacy collections) are handled correctly without manual intervention.queryparameter is ignored by all non-Qdrant backends; no behavioral change for Chroma, Milvus, pgvector, etc.fastembedwill download the selected sparse model on first use (default:Qdrant/bm25, ~20 MB). Ensure outbound network access or pre-cache the model in air-gapped deployments.Screenshots or Videos
Contributor License Agreement
🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.