DanLevy.net

Postgres टेक्स्ट सर्चिंग गाइड 2026

वे सर्च टूल्स जो पहले से आपके डेटाबेस में हैं, और हर एक कब अपना दाम निकालता है।

ज़्यादातर टीमें एक Postgres सर्च टूल इस्तेमाल करती हैं। जो टीमें तीनों जानती हैं, वे कम जटिलता के साथ बेहतर सर्च देती हैं — और उस समर्पित सर्च सर्विस से बचते हैं जिसकी उन्हें अभी ज़रूरत नहीं थी।

यह गाइड Postgres-native विकल्पों के पूरे सेट को कवर करती है: हर एक क्या करता है, यह कब सही फिट है, और इन्हें कैसे लेयर करना है।


तीन टूल्स

फुल-टेक्स्ट सर्च (tsvector / GIN इंडेक्स) शाब्दिक है। यह टेक्स्ट को लेक्सीम में टोकनाइज़ करता है, उन्हें स्टेम करता है, और क्वेरी को इंडेक्स के खिलाफ मैच करता है। “Running” और “runs” एक ही लेक्सीम में ढह जाते हैं। “dog” और “dogs” भी। रैंकिंग फंक्शन (ts_rank) उन डॉक्यूमेंट्स को इनाम देता है जहाँ क्वेरी टर्म्स बार-बार या प्रमुख रूप से दिखते हैं।

ट्राइग्राम (pg_trgm) स्ट्रिंग्स को ओवरलैपिंग 3-कैरेक्टर स्लाइस में तोड़ते हैं और यह मापते हैं कि दो स्ट्रिंग्स कितने स्लाइस शेयर करते हैं। “Dan” → " da", "dan", "an "। “Micheal” और “Michael” अपने ज़्यादातर ट्राइग्राम शेयर करते हैं, इसलिए समानता ज़्यादा है। यह pg_trgm को फज़ी नेम मैचिंग, टाइपो टॉलरेंस, और ऑटोकम्प्लीट के लिए बेहतरीन बनाता है — वह स्पेस जहाँ FTS खराब परफॉर्म करता है।

एक्ज़ैक्ट-मैच इंडेक्स (B-tree, hash) प्राइमरी कीज़, ईमेल एड्रेस, ID, SKU, और कुछ ऐसा संभालते हैं जहाँ जवाब बाइनरी है: मैच करता है या नहीं करता। ये “सर्च” जैसा फील नहीं करते, लेकिन यह बातचीत में शामिल हैं क्योंकि सबसे बुरा पैटर्न फज़ी या सेमांटिक सर्च का उपयोग उन समस्याओं के लिए करना है जिनके पास सही जवाब हैं।

चुनाव जटिलता के बारे में नहीं है। यह टूल को क्वेरी के आकार से मैच करने के बारे में है।

Postgres सर्च टूल मैपइनपुट शेप और क्वेरी इंटेंट के हिसाब से pg_trgm, फुल-टेक्स्ट सर्च, pgvector, और हाइब्रिड सर्च की तुलना।इनपुट शेप के हिसाब से सर्च प्रिमिटिव चुनेंएक ही Postgres टेबल चारों को सपोर्ट कर सकती है। ट्रिक है क्वेरी को टेक्स्ट से मैच करना।शब्द सही हैंमतलब ज़रूरी हैछोटा / स्ट्रक्चर्ड टेक्स्टलंबा प्रोज़ / चंक्सफज़ीpg_trgmनेम, एड्रेस, टाइटल, टाइपो,ऑटोकम्प्लीट, पार्शियल स्ट्रिंग्स।ऑर्थोग्राफिक समानता: स्पेलिंग दूरी।समानpgvectorरिलेटेड आइटम, डुप्लिकेट टिकट,छोटे डिस्क्रिप्शन से रिकमेंडेशन।एम्बेडिंग समानता: मतलब दूरी।शाब्दिकफुल-टेक्स्ट सर्चआर्टिकल्स, डॉक्स, लॉग्स, सपोर्ट कंटेंटजहाँ क्वेरी शब्द दिखने चाहिए।लेक्सीम, स्टेमिंग, रैंकिंग, बूलियन फिल्टर्स।हाइब्रिडFTS + pgvectorटेक्निकल डॉक्स और RAG जहाँ यूज़रकॉन्सेप्चुअल सवाल के साथ एक्ज़ैक्ट सिंबल पूछते हैं।दोनों रन करें, RRF से रैंक फ्यूज़ करें।पहले क्वेरी इंटेंट से शुरू करें, फिर टेक्स्ट शेप चेक करें
चार Postgres सर्च प्रिमिटिव्स, क्वेरी इंटेंट (एक्ज़ैक्ट vs सेमांटिक) और टेक्स्ट शेप (स्ट्रक्चर्ड vs प्रोज़) के हिसाब से मैप किए गए। एक ही टेबल चारों इंडेक्स ले जा सकता है — चुनाव पर क्वेरी है, टेबल पर नहीं।

जब फुल-टेक्स्ट सर्च जीतता है

प्रोज़ में कीवर्ड खोजना। ब्लॉग पोस्ट, डॉक्यूमेंटेशन, प्रोडक्ट डिस्क्रिप्शन, सपोर्ट टिकट, लीगल डॉक्यूमेंट्स। FTS इस शेप के कंटेंट के लिए डिज़ाइन किया गया था: नेचरल-लैंग्वेज टेक्स्ट पर इंडेक्स्ड, रैंक्ड रिट्रीवल।

कीवर्ड-बेस्ड यूज़र क्वेरीज़। यूज़र एक सर्च टर्म टाइप करते हैं, टैग से फिल्टर करते हैं, या कीवर्ड से ब्राउज़ करते हैं। FTS उस इंटेंट को बिना किसी एम्बेडिंग इन्फ्रास्ट्रक्चर के नेटिवली हैंडल करता है।

बाहरी डिपेंडेंसी के बिना रैंक्ड रिज़ल्ट्स। FTS इंडेक्स तेज़, डिटर्मिनिस्टिक हैं, और कोई API कॉल नहीं चाहिए। रिलेवेंस सिग्नल टर्म फ्रीक्वेंसी से आता है जो फील्ड पोज़िशन से वेटेड होता है।

बूलियन फिल्टरिंग के साथ सर्च। FTS आपके मौजूदा क्वेरी लॉजिक के साथ नेचुरलली कम्पोज़ होता है:

SELECT * FROM posts
WHERE search_vector @@ to_tsquery('english', 'postgres & performance')
AND category = 'tutorial'
AND published_at > NOW() - INTERVAL '6 months';

FTS सेटअप करना

-- जेनरेटेड कॉलम इंडेक्स को ऑटोमैटिकली अप टू डेट रखता है
ALTER TABLE posts ADD COLUMN search_vector tsvector
GENERATED ALWAYS AS (
setweight(to_tsvector('english', coalesce(title, '')), 'A') ||
setweight(to_tsvector('english', coalesce(body, '')), 'B')
) STORED;
CREATE INDEX posts_search_idx ON posts USING GIN (search_vector);
-- क्वेरी
SELECT title, ts_rank(search_vector, query) AS rank
FROM posts, to_tsquery('english', 'postgres & performance') query
WHERE search_vector @@ query
ORDER BY rank DESC
LIMIT 10;

setweight इम्पोर्टेंस असाइन करता है: A (टाइटल) B (बॉडी) से बड़ा है। यह ज़्यादातर कंटेंट-सर्च यूज़ केस के लिए पूरा रिलेवेंस मॉडल है।

जो FTS अच्छे से नहीं संभालता


जब ट्राइग्राम जीतते हैं (pg_trgm)

pg_trgm उस अजीब बीच को कवर करता है जो FTS लगातार फंबल करता है।

FTS टेक्स्ट को लेक्सीम में टोकनाइज़ करता है और स्टेम करता है। प्रोज़ के लिए यह सही है। नेम और शॉर्ट इडेंटिफायर के लिए अक्सर नहीं:

pg_trgm भाषा-अज्ञेय भी है, जो विविध भाषाई पृष्ठभूमि के नेम के लिए मायने रखता है। FTS को हर भाषा के लिए डिक्शनरी कॉन्फिगरेशन चाहिए।

फज़ी नेम सर्च

CREATE EXTENSION IF NOT EXISTS pg_trgm;
CREATE INDEX users_name_trgm_idx ON users USING GIN (name gin_trgm_ops);
-- "Michael Jordan" सर्च करते समय "Micheal Jordan" ढूंढता है
SELECT id, name, similarity(name, $1) AS score
FROM users
WHERE name % $1 -- % ऑपरेटर = समानता थ्रेशोल्ड (डिफॉल्ट 0.3)
ORDER BY score DESC
LIMIT 10;

% ऑपरेटर pg_trgm.similarity_threshold (डिफॉल्ट 0.3, रेंज 0–1) इस्तेमाल करता है। नेम सर्च के लिए, 0.3–0.4 टाइपो पकड़ता है जबकि नॉइज़ कम रखता है।

ऑटोकम्प्लीट, प्रीफिक्स, और कंटेंस सर्च

-- ऑटोकम्प्लीट के लिए प्रीफिक्स मैचिंग। ट्राइग्राम GIN इंडेक्स मदद कर सकता है,
-- लेकिन प्योर लेफ्ट-एंकर्ड प्रीफिक्स के लिए B-tree पैटर्न इंडेक्स बेहतर हो सकता है।
SELECT name FROM users
WHERE name ILIKE $1 || '%'
ORDER BY name
LIMIT 10;
-- लंबी स्ट्रिंग्स के अंदर पार्शियल मैच के लिए word_similarity
-- ("Andrew Johnson III" में से "Johnson")
SELECT id, name, word_similarity($1, name) AS score
FROM users
WHERE $1 <% name
ORDER BY score DESC
LIMIT 10;

ट्राइग्राम GIN इंडेक्स ILIKE '%pattern%' कंटेंस क्वेरीज़ और टाइपो-टॉलरेंट मैचिंग के लिए खासतौर पर उपयोगी है — पैटर्न जो बिना ट्राइग्राम इंडेक्स के आमतौर पर फुल-टेबल स्कैन होते हैं।

कब FTS के बजाय pg_trgm पकड़ना है

परिदृश्यउपयोग
टाइपो के साथ परसन/कंपनी नेम सर्चpg_trgm
ऑटोकम्प्लीट / प्रीफिक्स सर्चpg_trgm (या FTS प्रीफिक्स क्वेरीज़ के साथ)
छोटी स्ट्रिंग्स, इडेंटिफायर, कोडpg_trgm
प्रोज़ आर्टिकल्स, डॉक्स, टिकटFTS
कीवर्ड के लिए लॉग मैसेजFTS
मल्टीलिंगुअल नेम सर्चpg_trgm (भाषा-अज्ञेय)

जब एक्ज़ैक्ट-मैच SQL जीतता है

कुछ “सर्च” समस्याएँ सर्च नहीं हैं।

“ईमेल dan@example.com वाला यूज़र ढूंढो” इक्वैलिटी चेक है। “ऑर्डर ORD-12345 ढूंढो” प्राइमरी की लुकअप है। “तारीख के हिसाब से सॉर्ट किए गए tutorial कैटेगरी में पोस्ट्स लिस्ट करो” फिल्टर्ड क्वेरी है। ये B-tree या hash इंडेक्स पर हैं।

यहाँ FTS या ट्राइग्राम इस्तेमाल करने से जटिलता बढ़ती है बिना करेक्टनेस सुधारे — और एक्ज़ैक्ट इडेंटिफायर के लिए, नियर-मैच नो-मैच से बदतर है।

CREATE INDEX users_email_idx ON users (email);
-- एक्ज़ैक्ट लुकअप: तेज़ और स्पष्ट
SELECT id, name FROM users WHERE email = $1;

व्यापक पाठ: सही जवाब वाली समस्याओं के लिए अनुमानित सर्च एक कैटेगरी एरर है। यह कुछ रिटर्न करता है — जो कॉन्फिडेंटली गलत हो सकता है।


इन टूल्स को कंबाइन करना

ये टूल्स साफ़ तरीके से कम्पोज़ होते हैं। आपको बिल्कुल एक चुनना नहीं है।

फज़ी कीवर्ड वाले सर्च बॉक्स के लिए FTS + pg_trgm:

-- टाइटल में ट्राइग्राम समानता टाइपो पकड़ता है; ts_rank बॉडी रिलेवेंस हैंडल करता है
SELECT id, title,
ts_rank(search_vector, to_tsquery('simple', $1)) AS fts_rank,
similarity(title, $1) AS trgm_score
FROM posts
WHERE search_vector @@ to_tsquery('simple', $1)
OR title % $1
ORDER BY (ts_rank(search_vector, to_tsquery('simple', $1)) + similarity(title, $1)) DESC
LIMIT 10;

इंटरनेशनल कंटेंट के लिए FTS + unaccent:

-- डायाक्रिटिकल मार्क्स स्ट्रिप करें ताकि "José" "Jose" से मैच करे
CREATE EXTENSION IF NOT EXISTS unaccent;
CREATE TEXT SEARCH CONFIGURATION public.simple_unaccent (COPY = pg_catalog.simple);
ALTER TEXT SEARCH CONFIGURATION public.simple_unaccent
ALTER MAPPING FOR hword, hword_part, word
WITH unaccent, simple;
ALTER TABLE posts ADD COLUMN search_vector tsvector;
CREATE TRIGGER posts_search_vector_refresh
BEFORE INSERT OR UPDATE OF title, body ON posts
FOR EACH ROW EXECUTE FUNCTION
tsvector_update_trigger(search_vector, 'public.simple_unaccent', title, body);

इंटरनेशनल नेम सर्च के लिए unaccent + pg_trgm:

ALTER TABLE users ADD COLUMN name_search text;
CREATE FUNCTION users_name_search_refresh()
RETURNS trigger LANGUAGE plpgsql AS $$
BEGIN
NEW.name_search := unaccent(coalesce(NEW.name, ''));
RETURN NEW;
END;
$$;
CREATE TRIGGER users_name_search_refresh
BEFORE INSERT OR UPDATE OF name ON users
FOR EACH ROW EXECUTE FUNCTION users_name_search_refresh();
CREATE INDEX users_name_search_trgm_idx
ON users USING GIN (name_search gin_trgm_ops);
SELECT id, name
FROM users
WHERE name_search % unaccent($1)
ORDER BY similarity(name_search, unaccent($1)) DESC
LIMIT 10;

ट्रिगर के उदाहरण जेनरेटेड-कॉलम या इंडेक्स एक्सप्रेशन के अंदर unaccent() इस्तेमाल करने से बचते हैं, जहाँ PostgreSQL की इम्यूटेबिलिटी रूल्स मायने रखती हैं। अगर आप अपने इम्यूटेबल फंक्शन में unaccent() रैप करते हैं, तो दस्तावेज़ करें कि आप अपग्रेड/कॉन्फिगरेशन रिस्क स्वीकार कर रहे हैं।


उल्लेखनीय एक्सटेंशन

pg_trgm ज़्यादातर Postgres डिस्ट्रिब्यूशन के साथ बंडल आता है लेकिन एक्सप्लिसिट एनेबलमेंट चाहिए। Postgres में फज़ी स्ट्रिंग मैचिंग की नींव।

unaccent इंडेक्सिंग और क्वेरी से पहले डायाक्रिटिकल मार्क्स स्ट्रिप करता है। pg_trgm और FTS दोनों के साथ यूरोपीय-लैंग्वेज कंटेंट के लिए अच्छा जोड़ी। Postgres के साथ बंडल।

pg_bigm ट्राइग्राम एप्रोच को बाइग्राम (2-कैरेक्टर स्लाइस) तक बढ़ाता है, जो CJK (चीनी, जापानी, कोरियाई) भाषाओं के लिए pg_trgm के परफॉर्मेंस में ज़बरदस्त सुधार करता है। अलग से इंस्टॉल करना होगा; बंडल नहीं।

pg_search (ParadeDB से) स्टैंडर्ड GIN / tsvector स्टैक को Tantivy-बेस्ड BM25 इंडेक्स से बदलता है। यह BM25 स्कोरिंग (अक्सर ts_rank से बेहतर), FTS क्वेरीज़ के अंदर फज़ी मिलान, फैसेटेड सर्च, और बड़ी टेबल पर ड्रामेटिकली तेज़ इंडेक्सिंग देता है। जब स्टैंडर्ड FTS रैंकिंग या परफॉर्मेंस लिमिट दिखाने लगे तो यह ड्रॉप-इन अपग्रेड पाथ है।

-- pg_search: BM25 फुल-टेक्स्ट सर्च फज़ी मैचिंग के साथ
CREATE INDEX posts_bm25_idx ON posts
USING bm25 (id, title, body)
WITH (key_field = 'id', text_fields = '{"title": {}, "body": {}}');
-- BM25 स्कोरिंग + फज़ी मैचिंग के साथ क्वेरी ("javascipt" पकड़ता है)
SELECT id, title, paradedb.score(id) AS rank
FROM posts
WHERE posts @@@ paradedb.fuzzy_phrase(field => 'title', value => 'postgres performnce')
ORDER BY rank DESC
LIMIT 10;

pgvector घने वेक्टर स्टोरेज और समानता सर्च जोड़ता है। यह सही टूल है जब यूज़र जो चाहते हैं उसका नाम बताने के बजाय ब्याख्या करते हैं — सेमांटिक सर्च, RAG, रिलेटेड-कंटेंट रिकमेंडेशन, मल्टीलिंगुअल क्वेरी। Semantic Vector Search and Hybrid Strategies में गहराई से कवर किया गया है।


डिसीज़न टेबल

जो आप सर्च कर रहे हैंअनुशंसित
प्रोज़ आर्टिकल्स, डॉक्स, टिकटFTS
टाइपो के साथ परसन/कंपनी नेमpg_trgm
ऑटोकम्प्लीट, प्रीफिक्स सर्चpg_trgm
छोटे कोड, इडेंटिफायरpg_trgm
कीवर्ड के लिए लॉग मैसेजFTS
इंटरनेशनल नेमpg_trgm + unaccent
बड़ा कंटेंट, बेहतर रैंकिंगpg_search (ParadeDB BM25)
प्राइमरी कीज़, एक्ज़ैक्ट ईमेल, IDB-tree इंडेक्स
तारीख, रेंज, सॉर्टेड लिस्टB-tree इंडेक्स
परमिशन, कैटेगरी, फिल्टररेगुलर WHERE क्लॉज़
सवाल, पैराफ्रेज़, कॉन्सेप्टpgvector (अगला आर्टिकल देखें)

संदेह में: स्पेलिंग वेरिएशन के साथ छोटी स्ट्रिंग्स → ट्राइग्राम। कीवर्ड क्वेरी के लिए लंबा प्रोज़ → FTS। स्ट्रक्चर्ड इडेंटिफायर → रेगुलर इंडेक्स। कॉन्सेप्टुअल या नेचरल-लैंग्वेज क्वेरी → pgvector।


हाइब्रिड सर्च: दो सिग्नल, एक रैंक

जब "withRetry timeout errors" जैसी क्वेरी सर्च बॉक्स में आती है, इसमें दो तरह का इंटेंट होता है: एक्ज़ैक्ट सिंबल नेम जो यूज़र जानता है (withRetry) और कॉन्सेप्टुअल डिस्क्रिप्शन (timeout errors)। कोई एक प्रिमिटिव दोनों को कवर नहीं करता। FTS और वेक्टर सर्च को पैरलल में रन करना — फिर Reciprocal Rank Fusion से उनकी रैंक्ड लिस्ट मर्ज करना — करता है।

RRF हर रिज़ल्ट को 1 / (60 + rank) स्कोर करता है हर लिस्ट में और लिस्ट्स में सब करता है। कॉन्स्टेंट 60 टॉप रैंक के फायदे को डैम्प करता है, ताकि दोनों लिस्ट में दूसरे नंबर पर रहने वाला रिज़ल्ट एक लिस्ट में जीतने वाले और दूसरी में पूरी तरह मिस करने वाले को हरा सके। ज़रूरी बात: RRF कभी भी मेथड्स में रॉ स्कोर औसत नहीं करता — FTS रैंक और कोसाइन दूरी अलग करेंसी हैं और अरिथमेटिकली कंबाइन नहीं किए जा सकते।

Reciprocal Rank Fusion के साथ हाइब्रिड सर्चएक क्वेरी फुल-टेक्स्ट सर्च और वेक्टर सर्च में फैलती है, हर एक रैंक्स प्रोड्यूस करता है, और Reciprocal Rank Fusion उन्हें एक रिज़ल्ट लिस्ट में कंबाइन करता है।हाइब्रिड सर्च दो ईमानदार सिग्नल हैं, फिर एक मर्ज रैंकरॉ स्कोर औसत न करें। FTS रैंक और कोसाइन दूरी अलग करेंसी हैं।यूज़र क्वेरी”withRetrytimeout errors”FTS / BM25एक्ज़ैक्ट सिंबल और शब्द1. API रेफरेंस2. रिट्री गाइडpgvectorकॉन्सेप्टुअल नेबर्स1. नेटवर्क फेलर्स2. रिट्री गाइडRRF मर्जहर रिज़ल्ट को क्रेडिट दें उसकेजहाँ उसने हर लिस्ट में रैंक किया।1 / (60 + rank)फाइनल रिज़ल्ट्सटॉप हिट वह है जहाँ एक्ज़ैक्ट टर्म्सऔर सेमांटिक मतलब सहमत हैं।
एक क्वेरी FTS और pgvector में पैरलल फैलती है। हर एक अपनी अलग रैंक्ड लिस्ट प्रोड्यूस करती है। RRF हर डॉक्यूमेंट को उसकी पोज़िशन के हिसाब से स्कोर करता है हर लिस्ट में और स्कोर जोड़ता है — रिज़ल्ट उन डॉक्यूमेंट्स को सतह पर लाता है जिन पर दोनों सिग्नल सहमत हैं।
-- हाइब्रिड सर्च: RRF से मर्ज किया गया FTS + pgvector
WITH fts AS (
SELECT id, ts_rank(search_vector, query) AS score,
ROW_NUMBER() OVER (ORDER BY ts_rank(search_vector, query) DESC) AS rank
FROM docs, to_tsquery('english', 'withRetry & timeout') query
WHERE search_vector @@ query
LIMIT 60
),
vec AS (
SELECT id,
ROW_NUMBER() OVER (ORDER BY embedding <=> $embedding) AS rank
FROM docs
ORDER BY embedding <=> $embedding
LIMIT 60
)
SELECT COALESCE(fts.id, vec.id) AS id,
(COALESCE(1.0 / (60 + fts.rank), 0) +
COALESCE(1.0 / (60 + vec.rank), 0)) AS rrf_score
FROM fts FULL JOIN vec ON fts.id = vec.id
ORDER BY rrf_score DESC
LIMIT 10;

60-डॉक्यूमेंट कैंडिडेट पुल पर ब्रांच (LIMIT 60) एक कॉमन स्टार्टिंग पॉइंट है। रिकॉल कम हो तो चौड़ा करें; स्पीड के लिए संकुचित करें।


आगे क्या है

Postgres टेक्स्ट सर्च बहुत ज़मीन कवर करता है, लेकिन इसकी एक सीलिंग है। जब यूज़र जो चाहते हैं उसका नाम बताने के बजाय ब्याख्या करते हैं — “फ्लाइट में सोने में मदद करने वाला कुछ,” “डिबगिंग कॉन्फिडेंस के बारे में आर्टिकल्स एक नए इंजीनियर के रूप में” — शाब्दिक और ट्राइग्राम सर्च दोनों फेल होते हैं।

यह वेक्टर एम्बेडिंग, सेमांटिक सर्च, और हाइब्रिड आर्किटेक्चर का टेरिटरी है। Semantic Vector Search and Hybrid Strategies में कवर किया गया है।