What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

iTechGuides is reader-supported. When you buy through links on our site, we may earn an affiliate commission. As an Amazon Associate I earn from qualifying purchases. Learn more

To assess Postgres full-text search in production, test two things separately: whether the real search query meets your latency and throughput needs under realistic load, and whether its results are useful for representative user queries. A fast query can return poor matches; a relevant ranking can still be too slow. PostgreSQL’s built-in ranking functions are starting points, not universal relevance formulas.

What does a production search test need to answer?

A load test measures system behavior under a specified workload: observed latency, throughput, failures, and whether the database keeps up with the offered request rate. A relevance evaluation measures whether the returned documents—and their order—match what users mean. Keep the two results separate so a speed improvement does not conceal a worse result set.

PostgreSQL describes relevance as “vague and very application-specific.” Its built-in ranking functions use document and query information, but their scores are not globally calibrated probabilities. A score of 0.8 does not mean an 80% chance that a result is relevant. PostgreSQL 18: Controlling Text Search

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Freeze the experiment before comparing results

For a meaningful comparison, keep the environment and workload steady and change one search-design variable at a time. Record:

  • PostgreSQL major version and relevant database settings.
  • Text-search configuration, vector construction, index definition, and the data snapshot.
  • The exact search SQL, parameter values or query mix, filters, and result limit.
  • Client count, target transaction rate if rate control is used, test duration, and cache or warm-up conditions.
  • Observed latency distribution, throughput, failures, schedule lag, skipped work, and the execution-plan evidence.

Use the same dataset and environment for baseline and comparison runs. Planner estimates and timings depend on data statistics and the platform, so report results as measurements from the tested setup—not as universal PostgreSQL performance claims. PostgreSQL 18: Using EXPLAIN

Check what PostgreSQL is searching

PostgreSQL converts documents into tsvector values and user input into tsquery values. The text-search configuration controls parsing and dictionary normalization, including behavior such as stemming and stop words. If the configuration changes between runs, the set of matches can change too; hold it constant when you are comparing performance or relevance. PostgreSQL 18: Full Text Search

Inspect representative documents and queries rather than assuming the terms you expect are the terms PostgreSQL uses. For nullable source fields, make the null behavior explicit: PostgreSQL’s text-search documentation recommends coalesce when composing vectors from such fields.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
setweight(to_tsvector('english', coalesce(title, '')), 'A') ||
setweight(to_tsvector('english', coalesce(body, '')), 'D')

This illustrative expression gives title lexemes a different weight from body lexemes. Use the configuration and field weights that match your application, then verify how sample text is normalized and how the resulting vector behaves with the queries you expect to support. If you weight fields, make sure the query’s ranking calculation uses those weights.

Confirm the index and inspect the real query plan

GIN is PostgreSQL’s documented preferred text-search index type. A typical starting point is a GIN index on the stored search vector:

CREATE INDEX articles_search_vector_gin
ON articles USING GIN (search_vector);

Then inspect the actual search statement with representative parameters:

EXPLAIN (ANALYZE, BUFFERS)
SELECT id
FROM articles
WHERE search_vector @@ plainto_tsquery('english', 'sample search terms')
LIMIT 20;

EXPLAIN ANALYZE executes the statement and shows actual timings and row counts alongside planner estimates. Read the plan tree, including buffer activity, to find scans, rechecks, sorts, and other costly nodes. Estimates and cost units are not direct wall-clock predictions; interpret them in the context of the measured run. PostgreSQL 18: Using EXPLAIN

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Do not assume every query should use the index. Whether an index is useful depends on the data and query selectivity, so use the plan and measurements to determine what happened for your workload.

Index Documented behavior What to verify in your workload
GIN Preferred text-search index type. It stores lexemes rather than vector weight labels, so weighted searches may require heap-row rechecks. Plan nodes, rechecks, heap activity, search latency, index size and build cost, and the effect of updates. The cited documentation does not establish an application-independent result for these workload measures.
GiST Uses lossy signatures and can return false matches that PostgreSQL checks against the row. Plan nodes, rechecks, heap activity, search latency, index size and build cost, and the effect of updates. The cited documentation does not establish an application-independent result for these workload measures.

The index details above are documented on PostgreSQL 16’s text-search index page; check the documentation for the major version you deploy before relying on version-specific behavior. PostgreSQL 16: Preferred Index Types for Text Search

Load-test the search transaction, not a proxy workload

Use representative indexed data and run the real search statement, including the filters, ranking, ordering, and result limit used by the application. A built-in general-purpose benchmark workload is not a substitute for your search transaction. Create a custom pgbench script and use a realistic mix of terms and filters; vary concurrency and, separately, the target transaction rate.

For example, a custom script can contain the application’s search statement:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
SELECT id, ts_rank(search_vector, plainto_tsquery('english', 'sample terms')) AS rank
FROM articles
WHERE search_vector @@ plainto_tsquery('english', 'sample terms')
  AND status = 'published'
ORDER BY rank DESC
LIMIT 20;

Replace the illustrative terms, filter, and table details with the actual transaction and vary inputs to represent traffic. A test with one repeated query can overstate cache locality or miss expensive query classes.

When using rate control, record schedule lag as well as latency and throughput. Reported latency includes schedule lag; rising lag at the target rate means the offered workload is arriving faster than the tested setup can keep up with. Treat --latency-limit carefully: under rate control, transactions that have already exceeded the limit may be skipped rather than sent. Record skipped or late work instead of treating the latency summary as the whole result. PostgreSQL 18: pgbench

Run a baseline, change one variable—such as index type, vector construction, query construction, ranking function, or rank weights—and repeat the comparison. The appropriate latency threshold comes from your service’s own SLO; there is no universal target established here.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Evaluate relevance with judged queries

Build a small, representative set of user queries and judge the results each query should return. For each query, record relevant documents or graded judgments, then compare returned result sets and ordering against that same reference set. Agree in advance how the team will compare changes; the judgments and comparison method should reflect the application’s desired outcomes.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Use the set to compare query construction, field weights, document-length normalization, and ranking functions. ts_rank scores matching lexeme frequency; ts_rank_cd also considers proximity and requires positional information. Both offer weight and normalization options, so evaluate their effect on the judged queries rather than assuming one function is always more relevant. PostgreSQL’s normalization option 32 maps scores to a 0–1 range but does not change result ordering; it does not turn the score into a relevance percentage. PostgreSQL 18: Controlling Text Search

Ranking can add query work, so measure its cost in the load test as well as its effect on judged results. Keep the relevance decision distinct from the latency decision: adopt a ranking change only if its quality trade-off suits the application and its performance fits the service requirement.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.