AlloyDB AI hybrid search: RRF, RUM, and BM25 for RAG

Modern AI and RAG applications need at least two retrieval techniques to achieve high search relevance: vector search for semantic context and full-text search (FTS) for keyword precision. AlloyDB for PostgreSQL supports both, but managing them together has traditionally required a hands-on operational approach. The hard part is not executing the two searches; it is fusing the result sets. Merging vector distance scores with FTS relevance scores requires complex SQL or custom application code, often meaning a separate system for fusion, score normalization, and re-ranking.

Before the native solution, a robust hybrid search demanded multi-step orchestration: run the vector search (for example against a ScaNN index), run the FTS query (for example against a GIN index), normalize both result sets onto a common scale, then perform a FULL OUTER JOIN on document IDs in application memory, apply weighted summation of the normalized scores, and sort. The normalization step was described as brittle, prone to breaking when data distributions change. This decentralized approach led to fragile score logic, increased latency, high operational load, and reliance on application expertise to maintain search quality.

AlloyDB AI‘s hybrid search replaces that workflow with a single SQL function, hybrid_search(), powered by Reciprocal Rank Fusion (RRF). Because RRF is inherently rank-based, it bypasses score normalization entirely. The function accepts search components as a declarative JSON array and executes the entire workflow in a single query plan: it constructs dynamic SQL that creates a CTE per component using ROW_NUMBER() for positional rank, combines all ranked results with a FULL OUTER JOIN on document ID, and calculates the final unified score with the RRF formula at the kernel level. This minimizes overhead and helps ensure transaction consistency. The unified ai.hybrid_search API is not limited to two components, and it also natively supports external search sources via the FDW. RRF is the current ranking algorithm, with additional merging and ranking options planned.

For the text component, the article addresses a bottleneck in standard PostgreSQL GIN indexes: they do not store word positions, forcing costly table scans to re-analyze content for relevance ranking and phrase searches. The RUM extension, an index access method based on GIN, stores word positions directly in the index (word -> [(docID, [pos])] instead of word -> [docID]), so ranking, phrase, and proximity operations run predominantly inside the index and avoid heap scans. The reported benefits are significantly faster relevance ranking and efficient phrase and proximity searches. The trade-offs are slower index builds and a larger disk footprint, making RUM a good fit for ranking-heavy or high-concurrency workloads that prioritize query speed over write speeds and storage density. The article points to performance statistics on BEIR datasets showing speedups for RUM versus GIN on raw FTS queries and on hybrid search including FTS. RUM integrates with the <=> distance operator for search and ranking in hybrid SQL calls.

For teams that prioritize keyword scoring precision, a BM25 index is now in preview on AlloyDB and CloudSQL via the pg_textsearch extension. BM25 uses TF-IDF with term frequency saturation and document length normalization, delivering higher precision for keyword-based queries out of the box without external search engines or custom scoring logic.

The external search Foreign Data Wrapper (FDW) extends the same architecture to specialized clusters — Elasticsearch, OpenSearch, and Solr, to start. It lets users execute full-text searches against those backends from within the AlloyDB environment, with three stated advantages: optimized retrieval using the ranking algorithms and features of dedicated search backends, a unified PostgreSQL SQL interface for querying and joining external data, and portability for teams that keep existing search infrastructure. Getting started involves setting up a text-search index and a vector search index; for external search, you create a foreign data wrapper and foreign table. The article links to codelabs for hybrid search with Elasticsearch and Solr.

A comparison table in the article summarizes the operational effect: a multi-step application workflow (simulated with manual SQL) required complex normalization functions, service calls, and application joins, with constant tuning of normalization formulas; the AlloyDB-native path is a single declarative SQL call with zero external logic and no tuning, because RRF is rank-based and distribution-agnostic. The resulting architecture offers a dual path for FTS — RUM for low-latency internal search, or external search through FDW for specialized, scalable backends — both combinable with vector search in the same hybrid_search() call. The article’s closing point is that search relevance is less a technical problem than one of architectural simplicity and operational stability.

Simplify AI search with AlloyDB hybrid search and RRF

View Original