Rangordning och relevansjustering med ts_rank
Vikta dokumentavsnitt och finjustera rangordningsfunktioner så att de mest relevanta resultaten visas först.
Rangordning och relevansjustering med ts_rank är en gratis lektion i Prestandaoptimering och frågeoptimering i PostgreSQL på CoddyKit. Detta är lektion 2 av 4. Du kan läsa vilka 3 lektioner som helst i den här lärvägen kostnadsfritt i sin helhet – därefter låser CoddyKit PRO upp alla lektioner, plus praktisk övning med en inbyggd kodredigerare och en AI-lärare dygnet runt. Den ingår i lärvägen för Prestandaoptimering och frågeoptimering i PostgreSQL, och Era framsteg synkroniseras mellan webben och CoddyKit-appen. Kursen i Prestandaoptimering och frågeoptimering i PostgreSQL innehåller totalt 4 lektioner.
Varför rangordning är viktig
En fulltextfråga med @@ anger bara om ett dokument matchar en fråga, inte hur väl det matchar. För att visa de mest relevanta raderna först behöver du en rangordningsfunktion.
PostgreSQL levereras med två: ts_rank (frekvensbaserad) och ts_rank_cd (täckningsdensitet, tar hänsyn till termers närhet). Båda returnerar ett real-värde som du sorterar efter.
- Matchning är binär, snabb och stöds av index.
- Rangordning är en separat och dyrare beräkning som utförs på de matchade raderna.
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;Så poängsätter ts_rank
ts_rank baserar sin poäng på termfrekvens: hur ofta frågans lexem förekommer i dokumentet och vilka vikter de har tilldelats. Fler förekomster av en frågeterm innebär i allmänhet en högre poäng.
Det viktiga är att rangordningen beräknas mot tsvector, som lagrar lexempositioner. Ett dokument där termen förekommer fem gånger rankas högre än ett där den förekommer en gång, allt annat lika.
ts_rankignorerar hur nära termerna ligger varandra.ts_rank_cdpremierar dokument där frågetermerna ligger samlade.
Viktetiketterna A, B, C och D
Varje lexemposition i en tsvector kan ha en viktetikett: A, B, C eller D. Använd setweight() för att märka olika dokumentavsnitt, så att en träff i titeln räknas mer än en träff i brödtexten.
D är standardvärdet och den lägsta vikten. Konventionen är: A = titel, B = abstrakt/sammanfattning, C = brödtext, D = kommentarer eller metadata.
Du bygger en viktad vektor genom att sammanfoga anrop till setweight() med ||.
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');Lagra en viktad tsvector
För bättre prestanda förberäknar du den viktade tsvector-vektorn i en genererad kolumn och indexerar den med GIN. Då körs både rangordning och matchning mot samma viktade vektor, och texten tokeniseras aldrig igen vid frågetillfället.
Den genererade kolumnen beräknas automatiskt om när title eller body ändras, så den förblir konsekvent.
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);Finjustera vikter med arrayen
ts_rank accepterar ett valfritt första argument: en float4[] med fyra multiplikatorer för etiketterna i ordningen {D, C, B, A}. Observera ordningen – D kommer först och A sist.
Standardarrayen är {0.1, 0.2, 0.4, 1.0}. Höj multiplikatorn för A om du vill att titelträffar ska dominera ännu mer, eller gör arrayen jämnare för att minska effekten av avsnittsviktningen.
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;Normalisering efter längd
Som standard normaliserar ts_rank inte efter dokumentlängd, så långa dokument kan få högre poäng enbart för att de är långa. Det valfria sista heltalsargumentet styr normaliseringen via bitflaggor som du summerar.
0— ignorera längd (standard)1— dividera rangordningen med 1 + log(längd)2— dividera rangordningen med längden4— dividera med det harmoniska medelavståndet (endast cd)8— dividera med antalet unika ord16— dividera med 1 + log(unika ord)32— dividera med sig själv + 1 (mappar rangordningen till [0,1))
Tillämpa normalisering
Flaggan 1 är det vanligaste valet: den straffar långa dokument försiktigt med hjälp av en logaritm, så att en artikel på 2 000 ord inte kör över en fokuserad artikel på 200 ord. Kombinera flaggor genom att summera dem, till exempel 1|32 = 33 för att även mappa värdet till [0,1).
Ett normaliserat värde i intervallet [0,1) är praktiskt när du vill kombinera fulltextrangordning med andra signaler, till exempel aktualitet eller popularitet.
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 för närhet i fraser
ts_rank_cd implementerar rangordning med täckningsdensitet: den premierar dokument där frågans lexem förekommer nära varandra. Detta kräver positionsinformation, så funktionen fungerar bara på en tsvector som fortfarande har positioner (inte har rensats från dem).
För frågor som "query planner", där närhet signalerar relevans, överträffar ts_rank_cd vanligtvis vanliga ts_rank. Den accepterar samma viktarray och normaliseringsargument.
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;Prestandamönstret i två faser
Rangordning är CPU-bunden och körs för varje matchad rad, så låt den aldrig köras över miljontals rader. Det vinnande mönstret består av två faser: filtrera billigt med GIN-indexet och rangordna sedan bara de återstående raderna.
Placera @@-matchningen, som stöds av indexet, i en underfråga eller CTE, eventuellt med en grov LIMIT, och beräkna sedan ts_rank på den lilla kandidatmängden.
- Indexet reducerar miljontals rader till tusentals.
ts_ranksorterar sedan bara tusentals rader.
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;Rangordning kan inte indexeras
En vanlig missuppfattning är att ett GIN-index kan uppfylla ORDER BY ts_rank(...). Det kan det inte. GIN-index snabbar upp medlemskapstestet @@, men ts_rank är en svart låda vars värde inte lagras i indexet. PostgreSQL måste därför beräkna värdet och sedan sortera.
Om sorteringen efter rangordning är en flaskhals kan du bland annat förberäkna en statisk kvalitetskolumn, använda RUM-index (ett tillägg som kan returnera rader i rangordning) eller begränsa kandidatmängden först.
Kombinera rangordning med verksamhetssignaler
Ren textrangordning stämmer sällan med produktmässig intuition. Kombinera den normaliserade textpoängen med signaler som aktualitet och popularitet för att beräkna den slutliga ordningen. Eftersom flaggan 32 mappar textrangordningen till [0,1) kan den enkelt kombineras med andra normaliserade faktorer.
Behåll @@-filtret indexstödd; den kombinerade beräkningen körs bara på matchade kandidatrader.
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;Snabb kontroll
Du rangordnar sökresultat i en tabell med 5 miljoner rader och frågan är långsam. EXPLAIN visar en Bitmap Index Scan på GIN-indexet följd av en Sort på ts_rank(...). Vilken är den effektivaste lösningen?
Sammanfattning
Du har lärt dig att finjustera fulltextsökningens relevans i PostgreSQL:
- ts_rank poängsätter efter termfrekvens; ts_rank_cd premierar närhet (och behöver positioner).
- Märk avsnitt med
setweight()med etiketterna A/B/C/D och lagra den viktade vektorn i en GIN-indexerad genererad kolumn. - Viktarrayen
{D, C, B, A}(standardvärde{0.1,0.2,0.4,1.0}) styr avsnittens påverkan. - Normaliseringsflaggan styr längdstraff;
1tillämpar ett logaritmiskt straff och32mappar till[0,1)för kombination med andra värden. - Rangordning kan inte indexeras: filtrera alltid med
@@först och rangordna sedan den lilla kandidatmängden.
Lär dig SQL med en AI-lärare – gratis
Skriv och kör riktig kod i webbläsaren, få omedelbar hjälp av en AI-lärare dygnet runt och fortsätt där du slutade – på webben eller i appen.
- Kurser
- 22
- Lektioner
- 88
Vanliga frågor
Är lektionen ”Rangordning och relevansjustering med ts_rank” gratis?
Ja – du kan läsa vilka 3 lektioner som helst i lärvägen Prestandaoptimering och frågeoptimering i PostgreSQL, inklusive ”Rangordning och relevansjustering med ts_rank”, kostnadsfritt i sin helhet här på webben. Därefter låser CoddyKit PRO upp alla lektioner, plus interaktiv övning med en inbyggd kodredigerare och en AI-lärare dygnet runt. Kursen i Prestandaoptimering och frågeoptimering i PostgreSQL innehåller totalt 4 lektioner.
Vad lär jag mig i ”Rangordning och relevansjustering med ts_rank”?
Vikta dokumentavsnitt och finjustera rangordningsfunktioner så att de mest relevanta resultaten visas först. Ni övar på Prestandaoptimering och frågeoptimering i PostgreSQL med praktisk kod som körs direkt i webbläsaren, medan en AI-handledare som är tillgänglig dygnet runt svarar på Era frågor under lektionen.
Behöver jag någon erfarenhet för att börja lära mig Prestandaoptimering och frågeoptimering i PostgreSQL?
Du behöver inga förkunskaper. Utbildningen i Prestandaoptimering och frågeoptimering i PostgreSQL på CoddyKit är upplagd för allt från nybörjare till avancerade elever, så att du kan börja här eller från början och gå fram i din egen takt. Detta är lektion 2 av 4.
Hur lång tid tar lektionen ”Rangordning och relevansjustering med ts_rank”?
De flesta CoddyKit-lektioner tar cirka 5–10 minuter. Varje lektion är kort och interaktiv, så att du gör stadiga framsteg och kan fortsätta precis där du slutade – på webben eller i appen.
Kan jag skriva och köra kod i den här Prestandaoptimering och frågeoptimering i PostgreSQL-lektionen?
Ja. Varje Prestandaoptimering och frågeoptimering i PostgreSQL-lektion innehåller en inbyggd kodredigerare, så att du kan skriva och köra riktig kod direkt i webbläsaren och få omedelbar AI-feedback – utan lokal installation.
Alla lektioner i den här kursen
- Utforma tsvector-kolumner och GIN-index
- Rangordning och relevansjustering med ts_rank
- Luddig matchning med pg_trgm-likhet
- Kombinera filter med sökpredikat