Skip to main content

How to Grade Search Relevance Before You Touch Ranking Weights

Learn how to freeze your search corpus and score ranking relevance using graded judgments before modifying Postgres weights.

AI-written
Inewgen
12 Aug 2026Source: Dev.to3 min read (0 views)Last updated 29 Aug 2026
Share
How to Grade Search Relevance Before You Touch Ranking Weights

Stock photo for illustration only, not from the actual event

Font size
  • Never tune ranking weights against a live, constantly changing database table.
  • Freeze your corpus by creating a dated snapshot table as a consistent benchmark.
  • Source 30 to 50 real queries directly from search logs and support tickets.
  • Use nDCG@k metrics to evaluate and gate ranking ordering quality.

Before you change a single ts_rank weight, freeze your corpus and score the current ranking against a set of graded judgments. Otherwise, you are comparing two moving targets—new weights and new documents—and you will never know which one moved the needle. A relevance regression harness turns search complaints into a number you can diff, the same way a test suite turns broken code into a red check.

A previous discussion on load-testing a Postgres full-text index highlighted a critical point: if you tune weights against a live table, you are comparing two systems that are both changing at once. This post builds out the practical solution of how to actually collect judgments, freeze the corpus, and compute the scores.

Search quality lacks a single-row assertion. While a standard unit test asserts a concrete total like 42, relevance is about ordering. For example, for the query postgres connection pool, the pgbouncer guide should rank above an unrelated changelog entry. These orderings degrade quietly when a title weight fix inadvertently worsens three other queries without immediate notice.

Another common trap is the moving corpus where articles tables gain rows hourly. If you score today's ranking against today's table and last week's ranking against last week's table, any difference becomes contaminated by the documents themselves. You must hold the corpus still to isolate ranking changes.

The solution is to snapshot the exact columns your ranker reads into a dated table as your evaluation fixture:

-- One-time: capture the corpus as it was on this date.
CREATE TABLE eval_corpus_2026_08 AS
SELECT id, title, body, search_vector
FROM articles;

-- Run any candidate ranker against the frozen table, not the live one.
SELECT id
FROM eval_corpus_2026_08
WHERE search_vector @@ websearch_to_tsquery('english', :query)
ORDER BY ts_rank_cd(search_vector, websearch_to_tsquery('english', :query)) DESC
LIMIT 10;

When testing new weightings, you only change the ORDER BY clause and never the FROM clause. Keep the snapshot table in repository fixtures or a durable bucket so the baseline remains reproducible across any machine.

Never miss the latest news?

Subscribe to get news summaries by email - not often enough to be annoying.

โฆษณา

<
database query analytics chart workspace

Stock photo for illustration only, not from the actual event

You need a set of query, document, and grade rows where grades range from 0 for irrelevant up to 3 for perfect. Practical rules to manage this efficiently include:

  • Source queries directly from your logs and support tickets instead of imagination, as thirty to fifty real queries beat a thousand invented ones.
  • Judge a pool instead of the entire corpus by taking top results from every candidate ranker and grading only that union.
30-50Minimum real queries to capture broad regressions
10Top ranking results pooled for grading

Pick the metric matching your user interface usage, such as nDCG@k for typical article or documentation search. It rewards proper ordering of relevant grades while discounting gains down the page, normalized cleanly into a 0 to 1 range.

"relevance is a property of an ordering over a fixed corpus, so you cannot measure it without freezing the corpus first."

Dev.to

Source: Dev.to

Comments

Leave a Comment
0/2000

Found something wrong in this article? Report an issue with this article