Prestaties en queryoptimalisatie in PostgreSQL · Les

Ranking en relevantie afstemmen met ts_rank

Geef documentsecties gewichten en stem rankingfuncties af om de relevantste resultaten eerst te tonen.

Les 2 van 413 stappen

Ranking en relevantie afstemmen met ts_rank is een gratis Prestaties en queryoptimalisatie in PostgreSQL-les op CoddyKit. Dit is les 2 van 4. Je kunt 3 lessen uit dit leerpad gratis volledig lezen — daarna ontgrendelt CoddyKit PRO alle lessen, plus praktische oefeningen met een ingebouwde code-editor en een AI-tutor die 24/7 beschikbaar is. Deze les maakt deel uit van het leertraject Prestaties en queryoptimalisatie in PostgreSQL. Je voortgang wordt gesynchroniseerd op het web en in de CoddyKit-app. De cursus Prestaties en queryoptimalisatie in PostgreSQL bevat in totaal 4 lessen.

Waarom rangschikking belangrijk is

Een full-textquery met @@ vertelt je alleen of een document overeenkomt met een query, niet hoe goed. Om de relevantste rijen eerst te tonen, heb je een rangschikkingsfunctie nodig.

PostgreSQL levert er twee: ts_rank (op frequentie gebaseerd) en ts_rank_cd (op dekkingsdichtheid gebaseerd en rekening houdend met de nabijheid van termen). Beide geven een real-score terug waarop je kunt sorteren.

  • Overeenkomsten zoeken is binair, snel en wordt door de index ondersteund.
  • Rangschikken is een afzonderlijke, duurdere berekening die op de overeenkomende rijen wordt uitgevoerd.
SELECT title,
       ts_rank(to_tsvector('english', body), query) AS rank
FROM articles, to_tsquery('english', 'index & performance') query
WHERE to_tsvector('english', body) @@ query
ORDER BY rank DESC
LIMIT 10;

Hoe ts_rank scores berekent

ts_rank baseert zijn score op de frequentie van termen: hoe vaak de lexemen uit de query in het document voorkomen en welke weging ze hebben. Meer voorkomens van een queryterm betekent doorgaans een hogere score.

Belangrijk is dat de rang wordt berekend op basis van de tsvector, waarin posities van lexemen worden opgeslagen. Een document waarin de term vijf keer voorkomt, krijgt bij gelijkblijvende omstandigheden een hogere rang dan een document waarin de term één keer voorkomt.

  • ts_rank negeert hoe dicht termen bij elkaar staan.
  • ts_rank_cd beloont documenten waarin querytermen bij elkaar staan.

Wegingslabels A, B, C, D

Elke positie van een lexeem in een tsvector kan een wegingslabel hebben: A, B, C of D. Gebruik setweight() om verschillende documentsecties te markeren, zodat een overeenkomst in de titel zwaarder meetelt dan een overeenkomst in de hoofdtekst.

D is de standaardwaarde en heeft de laagste weging. De conventie is: A = titel, B = samenvatting, C = hoofdtekst, D = opmerkingen of metagegevens.

Je bouwt een gewogen vector door aanroepen van setweight() met || aan elkaar te koppelen.

SELECT setweight(to_tsvector('english', 'PostgreSQL Indexing'), 'A') ||
       setweight(to_tsvector('english', 'A guide to fast queries'), 'B') ||
       setweight(to_tsvector('english', 'Detailed body text about GIN indexes'), 'C');

Een gewogen tsvector opslaan

Bereken de gewogen tsvector voor betere prestaties vooraf in een gegenereerde kolom en indexeer deze met GIN. Zo worden zowel rangschikking als zoeken uitgevoerd op dezelfde gewogen vector en hoef je tijdens een query nooit opnieuw tokens te maken.

De gegenereerde kolom wordt automatisch opnieuw berekend wanneer title of body verandert, zodat deze consistent blijft.

ALTER TABLE articles
  ADD COLUMN search_vec tsvector
  GENERATED ALWAYS AS (
    setweight(to_tsvector('english', coalesce(title, '')), 'A') ||
    setweight(to_tsvector('english', coalesce(body, '')),  'C')
  ) STORED;

CREATE INDEX articles_search_idx ON articles USING GIN (search_vec);

Wegingen afstemmen met de array

ts_rank accepteert een optioneel eerste argument: een float4[] met vier vermenigvuldigingsfactoren voor de labels in de volgorde {D, C, B, A}. Let op de volgorde: D staat vooraan en A achteraan.

De standaardarray is {0.1, 0.2, 0.4, 1.0}. Verhoog de vermenigvuldigingsfactor voor A om overeenkomsten in de titel nog zwaarder te laten meetellen, of maak de array vlakker om de invloed van sectiewegingen te verminderen.

SELECT title,
       ts_rank('{0.1, 0.2, 0.4, 1.0}', search_vec, query) AS rank
FROM articles, to_tsquery('english', 'gin & index') query
WHERE search_vec @@ query
ORDER BY rank DESC
LIMIT 10;

Normalisatie van de lengte

Standaard normaliseert ts_rank niet voor de documentlengte. Lange documenten kunnen daardoor alleen al doordat ze lang zijn hogere scores opbouwen. Het optionele laatste gehele getal bepaalt de normalisatie via bitvlaggen die je bij elkaar optelt.

  • 0 — lengte negeren (standaard)
  • 1 — rang delen door 1 + log(lengte)
  • 2 — rang delen door lengte
  • 4 — delen door de gemiddelde harmonische afstand (alleen cd)
  • 8 — delen door het aantal unieke woorden
  • 16 — delen door 1 + log(unieke woorden)
  • 32 — delen door zichzelf + 1 (brengt de rang in kaart naar [0,1))

Normalisatie toepassen

Vlag 1 is de meest gebruikelijke keuze: deze bestraft lange documenten licht met behulp van een logaritme, zodat een artikel van 2000 woorden een gericht artikel van 200 woorden niet volledig overheerst. Combineer vlaggen door ze op te tellen, bijvoorbeeld 1|32 = 33 om de score ook in [0,1) te plaatsen.

Een score die naar [0,1) is genormaliseerd, is handig wanneer je de full-textrang wilt combineren met andere signalen, zoals recentheid of populariteit.

SELECT title,
       ts_rank(search_vec, query, 1) AS rank_lognorm,
       ts_rank(search_vec, query, 33) AS rank_0_to_1
FROM articles, to_tsquery('english', 'query & optimization') query
WHERE search_vec @@ query
ORDER BY rank_lognorm DESC
LIMIT 10;

ts_rank_cd voor nabijheid van woordgroepen

ts_rank_cd implementeert rangschikking op basis van dekkingsdichtheid: documenten waarin de lexemen uit de query dicht bij elkaar staan, krijgen een hogere waardering. Hiervoor is positie-informatie nodig, dus het werkt alleen op een tsvector waarin posities nog aanwezig zijn en niet zijn verwijderd.

Voor queries zoals "query planner", waarbij aangrenzende termen relevantie aangeven, presteert ts_rank_cd doorgaans beter dan gewone ts_rank. De functie accepteert dezelfde array met wegingen en dezelfde normalisatieargumenten.

SELECT title,
       ts_rank_cd(search_vec, query, 1) AS cd_rank
FROM articles,
     phraseto_tsquery('english', 'query planner') query
WHERE search_vec @@ query
ORDER BY cd_rank DESC
LIMIT 10;

Het prestatiepatroon in twee fasen

Rangschikking is CPU-gebonden en wordt per overeenkomende rij uitgevoerd. Laat deze dus nooit over miljoenen rijen lopen. Het effectieve patroon bestaat uit twee fasen: filter goedkoop met de GIN-index en rangschik daarna alleen de overgebleven rijen.

Plaats de door de index ondersteunde @@-overeenkomst in een subquery of CTE, eventueel met een grove LIMIT, en bereken daarna ts_rank op die kleine verzameling kandidaten.

  • De index verkleint miljoenen rijen tot duizenden.
  • ts_rank sorteert vervolgens alleen die duizenden rijen.
WITH candidates AS (
  SELECT id, title, search_vec
  FROM articles
  WHERE search_vec @@ to_tsquery('english', 'index & tuning')
  LIMIT 500
)
SELECT id, title,
       ts_rank(search_vec, to_tsquery('english', 'index & tuning')) AS rank
FROM candidates
ORDER BY rank DESC
LIMIT 10;

Rangschikking kan niet door een index worden ondersteund

Een veelvoorkomende misvatting is dat een GIN-index kan voldoen aan ORDER BY ts_rank(...). Dat kan niet. GIN-indexen versnellen de lidmaatschapstest @@, maar ts_rank is een black-boxfunctie waarvan de waarde niet in de index wordt opgeslagen. PostgreSQL moet de waarde dus berekenen en daarna sorteren.

Als sorteren op rangschikking een knelpunt is, kun je onder andere een statische kwaliteitsscore vooraf berekenen in een kolom, RUM-indexen gebruiken (een uitbreiding die rijen wel in rangorde kan teruggeven) of eerst het aantal kandidaten beperken.

Rangschikking combineren met zakelijke signalen

Een pure textrang sluit zelden aan bij de intuïtie van een product. Combineer de genormaliseerde tekstscore met signalen zoals recentheid en populariteit om een uiteindelijke volgorde te berekenen. Omdat vlag 32 de textrang naar [0,1) omzet, kun je deze eenvoudig combineren met andere genormaliseerde factoren.

Houd het @@-filter door de index ondersteund; de berekening van de gecombineerde score wordt alleen uitgevoerd op overeenkomende kandidaatrijen.

SELECT id, title,
       ts_rank(search_vec, query, 32) AS text_score,
       ts_rank(search_vec, query, 32) * 0.7
         + (1.0 / (1 + extract(epoch FROM now() - created_at) / 86400)) * 0.3
         AS final_score
FROM articles, to_tsquery('english', 'postgres & performance') query
WHERE search_vec @@ query
ORDER BY final_score DESC
LIMIT 10;

Snelle controle

Je rangschikt zoekresultaten in een tabel met 5 miljoen rijen en de query is traag. EXPLAIN toont een Bitmap Index Scan op de GIN-index, gevolgd door een Sort op ts_rank(...). Wat is de effectiefste oplossing?

Samenvatting

Je hebt geleerd hoe je relevantie bij full-textzoeken in PostgreSQL afstemt:

  • ts_rank scoort op basis van de frequentie van termen; ts_rank_cd beloont nabijheid en heeft posities nodig.
  • Markeer secties met setweight() en de labels A/B/C/D, en sla de gewogen vector op in een met GIN geïndexeerde gegenereerde kolom.
  • Met de array voor wegingen {D, C, B, A} (standaard {0.1,0.2,0.4,1.0}) stem je de invloed van secties af.
  • De normalisatievlag bepaalt de lengtecorrecties; 1 past een logaritmische correctie toe en 32 brengt de score in [0,1) voor combinaties.
  • Rangschikking kan niet door een index worden ondersteund: filter altijd eerst met @@ en rangschik daarna de kleine verzameling kandidaten.
Gratis beginnen

Leer SQL met een AI-tutor — gratis

Schrijf echte code en voer die uit in je browser, krijg direct hulp van een AI-tutor die 24/7 beschikbaar is en ga verder waar je gebleven bent op het web of in de app.

Cursussen
22
Lessen
88

Veelgestelde vragen

Is de les “Ranking en relevantie afstemmen met ts_rank” gratis?

Ja — je kunt hier op het web alle 3 lessen van het leerpad Prestaties en queryoptimalisatie in PostgreSQL, waaronder “Ranking en relevantie afstemmen met ts_rank”, gratis volledig lezen. Daarna ontgrendelt CoddyKit PRO alle lessen, plus interactieve oefeningen met een ingebouwde code-editor en een AI-tutor die 24/7 beschikbaar is. De cursus Prestaties en queryoptimalisatie in PostgreSQL bevat in totaal 4 lessen.

Wat leer ik in “Ranking en relevantie afstemmen met ts_rank”?

Geef documentsecties gewichten en stem rankingfuncties af om de relevantste resultaten eerst te tonen. Je oefent met Prestaties en queryoptimalisatie in PostgreSQL door code rechtstreeks in de browser uit te voeren. Een AI-begeleider die 24/7 beschikbaar is beantwoordt je vragen terwijl je de les doorwerkt.

Heb ik ervaring nodig om met Prestaties en queryoptimalisatie in PostgreSQL te beginnen?

Ervaring vooraf is niet nodig. Prestaties en queryoptimalisatie in PostgreSQL op CoddyKit is opgebouwd voor beginners tot gevorderden, zodat je hier of bij het begin kunt starten en in je eigen tempo kunt leren. Dit is les 2 van 4.

Hoe lang duurt de les “Ranking en relevantie afstemmen met ts_rank”?

De meeste lessen van CoddyKit duren ongeveer 5–10 minuten. Elke les is kort en interactief, zodat je gestaag vooruitgaat en op het web en in de app precies verdergaat waar je was gebleven.

Kan ik code schrijven en uitvoeren in deze les over Prestaties en queryoptimalisatie in PostgreSQL?

Ja. Elke les over Prestaties en queryoptimalisatie in PostgreSQL bevat een ingebouwde code-editor, zodat je rechtstreeks in je browser echte code kunt schrijven en uitvoeren en direct feedback van AI krijgt — lokale installatie is niet nodig.

Alle lessen in deze cursus

  1. tsvector-kolommen en GIN-indexen ontwerpen
  2. Ranking en relevantie afstemmen met ts_rank
  3. Fuzzy matching met pg_trgm-similarity
  4. Filters combineren met zoekpredicaten
← Terug naar Prestaties en queryoptimalisatie in PostgreSQL