Comparing Weaviate, OpenSearch, and PostgreSQL with pgvector for Vector and Hybrid Search

When an application requires search functionality, common options include tools that may already be part of the existing infrastructure, such as OpenSearch for full-text search or PostgreSQL with the pgvector extension for applications centered on relational data. Both systems can store and query vectors, but neither was originally designed around vectors as its primary data structure. OpenSearch introduced vector search on top of an inverted-index search engine. pgvector provides nearest-neighbor search through SQL operators, which works well when vectors are simply one column among many, but it was not designed specifically for applications where semantic search is the central workload.

Weaviate approaches the problem differently. Its HNSW (Hierarchical Navigable Small World) vector index is a core data structure, while BM25 keyword search and hybrid search are integrated around it rather than added later. In practical terms, semantic search, keyword search, and a combination of both can each be performed through a single API call. When Weaviate is operated through a managed database platform, infrastructure tasks such as provisioning, backups, cluster maintenance, and software updates can be handled by the service provider, reducing the operational work required from application teams.

To make the comparison concrete, this article loads the same corpus of podcast transcripts into three managed database environments and compares the characteristics that actually distinguish them for search workloads. These dimensions include the quality of results from vector, keyword, and hybrid retrieval, the amount of search logic that must remain inside the application, ingest speed, and index size. Raw query speed is not a focus. At this scale, all three systems respond at comparable speeds, making query latency an insufficient differentiator.

Key Takeaways

Hybrid search produced the best quality for every engine, while only Weaviate performed it through a single API call. Combining vector and keyword retrieval outperformed either method individually across all three systems, with Weaviate achieving the highest hybrid score at hit@10 0.725, followed by OpenSearch at 0.685 and PostgreSQL with pgvector at 0.665. With OpenSearch and pgvector, hybrid retrieval requires two rankings together with a rank-fusion process that must be implemented and maintained separately.

Vector-search quality is primarily determined by the embeddings rather than by the database. When the candidate-list depth was aligned across the engines, all three produced results within hit@10 0.575–0.600 while using the same vectors.

The operational advantages differ between engines. Weaviate ingested the data fastest because its HNSW and BM25 indexes were created together during a single loading process. pgvector produced the smallest storage footprint, with a 1.87 GB index compared with 2.91 GB for OpenSearch, although its keyword-search behavior required deliberate query construction to achieve competitive results.

Methodology

The dataset consisted of podcast-episode transcripts from 4,886 unique episodes. The transcripts were divided into passages of approximately 500 tokens, resulting in 100,000 documents. Each document contained the passage text together with episode metadata. Embeddings were generated once with OpenAI’s text-embedding-3-small model using 1,536 dimensions and then cached, ensuring that the embedding process remained identical across all three systems.

All three engines ran as managed database services and were queried from a separate cloud compute instance.

Service Engine / Index
Managed Weaviate HNSW (vector) + BM25 (keyword), native hybrid
Managed OpenSearch k-nearest neighbors (k-NN) Lucene HNSW + inverted index
Managed PostgreSQL + pgvector HNSW (vector_cosine_ops) + GIN tsvector

Two primary index types appear throughout this comparison. HNSW (Hierarchical Navigable Small World) is a graph-based structure designed for vector search. It connects each embedding with nearby embeddings inside a layered graph. Instead of comparing a query against all 100,000 vectors, the search engine navigates through the graph to identify vectors that are close to the query. This makes retrieval fast, although the result is approximate rather than exhaustive.

An inverted index is the traditional structure used for keyword search. It maps individual words to the passages in which those words occur, similar to how the index at the end of a book maps terms to page numbers. BM25 is a standard ranking formula that evaluates those matches according to factors such as how uncommon a term is and how strongly it is concentrated within a passage. PostgreSQL provides comparable functionality through pgvector’s HNSW implementation for vectors and a GIN (Generalized Inverted Index) over tsvector, PostgreSQL’s native full-text-search type, for keyword retrieval.

This comparison is not intended to be a controlled hardware benchmark. The underlying instances were not matched to identical specifications, so direct head-to-head performance claims are deliberately avoided. The emphasis is instead placed on result quality and architectural differences that remain relevant regardless of the specific hardware, particularly how each system performs retrieval and how much search-related logic the application must implement.

To measure retrieval quality, 200 passages were selected randomly from the dataset. An LLM (large language model) generated a natural-language question that was specific to each selected passage. Those generated questions were then used as search queries to determine whether the original passage appeared among the ten highest-ranked results.

A successful retrieval was counted when the source passage appeared within the top ten results, producing the hit@10 metric. Mean reciprocal rank (MRR) was also recorded, assigning a higher score when the relevant passage appeared nearer to the top of the ranking. Recall@10 was measured against an exact brute-force nearest-neighbor search performed over the same vectors. Index size represents the on-disk storage footprint after the complete dataset had been loaded.

For index configuration, Weaviate and pgvector used HNSW with default parameters, while OpenSearch used its k-NN functionality with the Lucene HNSW engine and cosine similarity. All vectors were pre-computed and remained identical between systems. To make the engines comparable, each was configured in a way representative of how a practitioner might configure it for English-language content.

Weaviate’s keyword operator is technically BM25F, which supports weighting across multiple fields. When only one content field is searched, however, its behavior reduces to standard BM25, matching the general algorithm used by OpenSearch. OpenSearch used the English analyzer for stopword removal and stemming, corresponding to the English-language configuration in PostgreSQL and the tokenization behavior used by Weaviate. OpenSearch was also force-merged after loading in accordance with guidance for its k-NN implementation. The HNSW candidate-list depth, represented by ef_search, was aligned at 100 for all three systems, which corresponds to the default used in Weaviate.

Ingest and Loading

Because embeddings were pre-computed, the loading phase performed essentially the same task in each system: inserting 100,000 passages and constructing both a vector index and a keyword index. Since the underlying instances were not matched to identical specifications, the exact loading times should not be treated as transferable hardware benchmarks. However, the observed order, with Weaviate first, PostgreSQL second, and OpenSearch third, reflects differences in how the engines construct and maintain their indexes.

Weaviate performs ingestion and indexing together. As each object enters through its gRPC batch endpoint, the corresponding HNSW graph node and BM25 inverted-index entry are created during the same process. Once loading is complete, the collection is already searchable. No separate indexing procedure needs to be initiated, scheduled, or monitored.

PostgreSQL separates data loading from index creation. Bulk inserts using execute_values can place rows into the database quickly, after which the HNSW and GIN indexes are created as separate operations. For this dataset of relatively small passages, those index-building steps were short. With larger datasets, however, constructing the HNSW index can become the dominant part of the process. That step must be initiated, monitored, and repeated deliberately whenever a complete reload is performed.

OpenSearch incurs indexing work repeatedly while data is being loaded. Its k-NN structures are rebuilt as segments merge during ingestion, meaning index maintenance is interleaved with the bulk-loading process and portions of the work are repeated. In this comparison, that behavior resulted in the slowest ingestion of the three systems. Configuration changes such as larger segments or delayed refresh operations can recover part of that time, but those optimizations add additional operational decisions that must be managed externally.

The important distinction is therefore not simply raw insert speed. It is where the index-building work occurs: automatically during ingestion in Weaviate, as an explicit post-load operation in PostgreSQL, or distributed throughout the ingestion process in OpenSearch.

Hybrid Search: One Call vs. Two

Hybrid search combines semantic vector relevance with BM25 keyword relevance. It produced the strongest result quality in this comparison and also highlights some of the most important architectural differences between the three systems. The practical issue is how much of the retrieval workflow must be implemented and maintained by the application.

Weaviate supports hybrid search natively through a single call. It searches both the HNSW and BM25 indexes, combines their rankings with Reciprocal Rank Fusion (RRF), and returns a unified ranked list. The alpha parameter determines the balance between the two retrieval methods, where 0 represents pure keyword search and 1 represents pure vector search.

results = collection.query.hybrid(
query=query_text,
vector=query_vector,
alpha=0.5,
limit=10,
)

OpenSearch does not provide a native hybrid query in this configuration. A vector query and a keyword query are executed separately, after which the resulting ranked lists are combined within the application.

knn = client.search(index=”passages”, body={
“size”: 50, “query”: {“knn”: {“embedding”: {
“vector”: query_vector, “k”: 50,
“method_parameters”: {“ef_search”: 100}}}}})
bm25 = client.search(index=”passages”, body={
“size”: 50, “query”: {“match”: {“transcript_text”: query_text}}})

def rrf(*result_lists, k=60): # you write, test, and maintain this
scores = {}
for results in result_lists:
for rank, hit in enumerate(results):
scores[hit[“_id”]] = scores.get(hit[“_id”], 0) + 1 / (k + rank + 1)
return sorted(scores, key=scores.get, reverse=True)[:10]

results = rrf(knn[“hits”][“hits”], bm25[“hits”][“hits”])

pgvector does not provide a dedicated hybrid-search primitive either. The equivalent two-ranking fusion process can instead be implemented directly in SQL.


WITH v AS (SELECT guid, row_number() OVER (ORDER BY embedding <=> %(qv)s) rk
FROM passages ORDER BY embedding <=> %(qv)s LIMIT 50),
k AS (SELECT guid, row_number() OVER (ORDER BY ts_rank(tsv, to_tsquery(‘english’, %(q)s)) DESC) rk
FROM passages WHERE tsv @@ to_tsquery(‘english’, %(q)s) LIMIT 50)
SELECT p.guid,
COALESCE(1.0/(60+v.rk),0) + COALESCE(1.0/(60+k.rk),0) AS rrf_score
FROM passages p LEFT JOIN v USING (guid) LEFT JOIN k USING (guid)
WHERE v.guid IS NOT NULL OR k.guid IS NOT NULL
ORDER BY rrf_score DESC LIMIT 10;
System How Hybrid Works Network Round Trips Fusion Logic You Maintain
Weaviate Native, one call One None (alpha parameter)
OpenSearch Two queries + client merge Two RRF function in application code
pgvector Two rankings + merge in SQL One RRF expression in every query

The difference extends beyond network round trips. A two-ranking architecture introduces an additional maintenance surface. Fusion logic becomes part of the application’s codebase, must be tested, and must remain consistent across every service and programming language that performs search. With Weaviate, adjusting the blend requires changing a single floating-point parameter. With the other approaches, the equivalent behavior depends on fusion logic and smoothing constants that the application team must own.

Search Quality: Vector vs. Keyword vs. Hybrid

Using the 200 generated questions described in the methodology, each engine was evaluated according to whether the original source passage appeared in its ten highest-ranked results, measured with hit@10, and according to mean reciprocal rank (MRR).

Engine Vector (hit@10 / MRR) Keyword Hybrid
pgvector 0.575 / 0.376 0.550 / 0.357 0.665 / 0.446
Weaviate 0.600 / 0.399 0.660 / 0.465 0.725 / 0.504
OpenSearch 0.580 / 0.382 0.635 / 0.475 0.685 / 0.450

Three findings are particularly important.

Hybrid retrieval achieved a higher hit@10 score than either vector or keyword retrieval alone for every engine. This is the most directly applicable result from the comparison. When retrieval quality is important, combining the two search modalities provides a clear advantage.

Vector-search quality was nearly identical across the three systems, ranging from hit@10 0.575 to 0.600. This is expected because all three stored the same embeddings and, once candidate depth was aligned, retrieved them with very similar recall of 0.967–0.988. The database itself does not improve or reduce the semantic information represented by the vectors. Its responsibility is to retrieve the appropriate vectors reliably.

Weaviate produced the strongest hybrid result and the highest overall score, reaching hit@10 0.725 and MRR 0.504. The result reflects the combination of its native fusion mechanism and BM25 implementation.

PostgreSQL full-text retrieval initially produced a score of 0.04 when using plainto_tsquery, which requires all relevant words from a question to match. Reconstructing the query so its terms were combined with OR through to_tsquery and the | operator increased the result to 0.550. OpenSearch and Weaviate produced stronger keyword behavior with their default configurations, while PostgreSQL required deliberate construction of the text query.

What the Numbers Look Like for One Query

Aggregate metrics can be difficult to interpret in isolation, so it is useful to examine what the systems returned for an individual query: how to handle rejection in sales. Because the indexed documents were transcript passages taken from the middle of conversations, some results begin in the middle of a sentence.

Vector search returned the same top three results across all three engines because each system contained identical embeddings. The strongest result came from an interview with Daniel Pink about his sales book To Sell Is Human:

“…gonna get rejected, there’s no question about it. So one of the qualities that I talk about is a quality called buoyancy… every day I face an ocean of rejection. Okay, that’s what sales is like… So buoyancy is how do you stay afloat in that ocean of rejection?”

The selected database does not determine what semantic information the embedding model identifies. That behavior is primarily influenced by the embedding model. The database instead affects how reliably the corresponding vectors are retrieved, which is reflected in recall.

Keyword retrieval also produced agreement near the top for this query. All three engines ranked the same two passages in the first positions: a sales professional describing early cold-calling experience and an Art of Charm listener question concerning anxiety around cold calls. The systems began to diverge in the third position, where each selected a different passage while remaining relevant to the topic.

Engine Third Keyword Result Relevance
Weaviate the Daniel Pink buoyancy passage above on topic ✓
pgvector 30 Minutes to President’s Club, an SDR (Sales Development Representative) cold-call training story on topic ✓
OpenSearch a solo episode on avoiding new work for fear of rejection on topic ✓

The differences are not always this minor. For another question among the 200 evaluation queries, “What should a presenter do if their audience starts leaving during their session?”, only Weaviate returned the passage from which the question had originally been generated. The other two systems produced the same incorrect result.

Engine Top Keyword Result Evaluation
Weaviate The Art of Charm, featuring a speaking coach addressing exactly that situation: “…with everybody leaving, I would probably say to the organizer… do we need to reschedule my session? Is there something we need to do to handle this?…” the source passage ✓
pgvector a musician discussing when to stop playing a song that is not connecting with the audience wrong domain ✗
OpenSearch the same musician passage wrong domain ✗

Field weighting and analyzer configuration, rather than the BM25 formula by itself, determine whether keyword retrieval remains focused on the intended topic. Keyword search evaluates words rather than understanding their meaning, so the fields and tokens included in ranking have a substantial effect.

Hybrid search combines semantic and lexical signals. For the sales-rejection query, all three systems converged on the same three leading passages: the sales professional’s cold-calling story, the Daniel Pink passage about buoyancy, and the question about cold-call anxiety. This reflects the broader pattern visible in the aggregate results. Vector retrieval remains comparatively consistent between engines, keyword retrieval introduces more variation, and hybrid retrieval provides the most dependable results for practical search queries.

Index Size

The following figures represent the storage footprint after all 100,000 documents had been loaded with 1,536-dimensional embeddings, passage text, and metadata.

System Index Size Notes
PostgreSQL + pgvector 1.87 GB 781 MB HNSW + 42 MB GIN full-text + table
OpenSearch 2.91 GB k-NN graph + inverted index + raw vectors stored in _source
Weaviate not exposed The managed Weaviate environment does not expose on-disk size through the API

pgvector produced the smallest storage footprint, which is a meaningful benefit when vectors exist as one column within an existing relational table. OpenSearch required more storage partly because the raw embedding array remains inside each document’s _source in addition to the k-NN graph. Weaviate also maintains an HNSW graph together with an inverted index, but the managed environment used for this comparison did not expose a disk-size measurement, so no estimate was included.

When to Use Each Search Engine

When to Use Weaviate

Weaviate is a strong choice when semantic search or retrieval-augmented generation (RAG) is the primary workload. Native hybrid retrieval, the highest ingestion throughput observed in this comparison, and the strongest retrieval quality make it a lower-maintenance option when search functionality is central to the application. Its alpha parameter replaces a separate fusion function and the surrounding application logic. When deployed through a managed platform, Weaviate can also be provisioned without requiring index-management operations to be implemented in application code.

When to Use OpenSearch

OpenSearch is well suited to environments that already operate an OpenSearch cluster for full-text search or log analytics and want to introduce vector retrieval incrementally. It is also appropriate when mature capabilities such as faceting, aggregations, and horizontal scaling are important. Its results are competitive, but hybrid retrieval requires application-side orchestration in this configuration. The selected k-NN engine can also significantly affect recall and tuning behavior, making that architectural decision important early in the implementation.

When to Use PostgreSQL with pgvector

PostgreSQL with pgvector is a strong option when vectors are secondary to a relational schema. Examples include passage or product tables where ORDER BY embedding <=> $1 is only one component among multiple WHERE clauses, JOIN operations, and transactional requirements. It produced the smallest storage footprint in this comparison and keeps vectors close to the relational records they describe. The trade-offs are keyword retrieval that requires deliberate query construction and hybrid ranking that must be implemented directly in SQL.

Conclusion

All three systems can competently store and query vectors, and at the evaluated scale their response times were broadly comparable. The more important differences appear in the quality of the returned results and in the amount of search-specific logic that application developers must implement. Hybrid retrieval delivered the strongest overall quality across the systems. pgvector required careful query construction for effective keyword retrieval, OpenSearch required deliberate analyzer and recall configuration, and Weaviate produced the highest retrieval quality in this comparison while requiring the least custom hybrid-search code.

For applications where search is one feature alongside relational data, pgvector can avoid introducing another service and allows vectors to remain close to the data they represent, while requiring the application to implement more of the keyword and hybrid-search behavior. For applications in which search itself is a central product capability, implementing hybrid retrieval with OpenSearch or pgvector introduces additional operational work through separate retrieval paths, fusion logic, and consistent tuning across clients. Weaviate replaces much of that implementation with a single hybrid query and one weighting parameter while producing the strongest retrieval quality observed in this comparison.

Source: digitalocean.com

Create a Free Account

Register now and get access to our Cloud Services.

Posts you might be interested in: