- Postgres के भीतर semantic, full-text, और fuzzy search — तीनों को मिलाकर एक hybrid search engine बनाया जा सकता है
- search कई apps का अहम हिस्सा है, लेकिन इसे सही तरह implement करना आसान नहीं है। खासकर RAG pipelines में search quality पूरे process की सफलता या विफलता तय कर सकती है
- semantic search भले ही ट्रेंडी हो, लेकिन पारंपरिक lexical search अब भी search की रीढ़ है
- semantic techniques results बेहतर कर सकती हैं, लेकिन वे मजबूत text-based search की नींव पर सबसे अच्छा काम करती हैं
Postgres के साथ search engine बनाना
- तीन तकनीकों का संयोजन:
tsvector के साथ full-text search
pgvector के साथ semantic search
pg_trgm के साथ fuzzy matching
- यह approach हर स्थिति में बिल्कुल सबसे बेहतर हो, ऐसा जरूरी नहीं, लेकिन अलग search service बनाने का एक शानदार विकल्प है
- मौजूदा Postgres database के भीतर implement और scale करने के लिए यह एक मजबूत शुरुआती बिंदु है
- क्यों Postgres को हर चीज़ के लिए इस्तेमाल करना चाहिए: बस हर जगह Postgres इस्तेमाल करें, PostgreSQL ही काफी है, बस Postgres इस्तेमाल करें
FTS और semantic search implement करना
- Supabase के पास hybrid search implement करने पर बेहतरीन docs हैं, इसलिए इसे शुरुआती आधार के रूप में लिया जा सकता है
- guide के अनुसार GIN index से FTS implement किया जाता है, और pgvector (जिसे bi-encoder dense retrieval भी कहा जाता है) से semantic search
- व्यक्तिगत अनुभव में 1536-dimensional embeddings चुनने से काफी बेहतर results मिल सकते हैं
- Supabase functions को CTEs और queries से replace किया गया है, और parameters के आगे
$ लगाया गया है
- यहाँ results को merge करने के लिए RRF(Reciprocal Ranked Fusion) का उपयोग किया गया है
- यह तरीका सुनिश्चित करता है कि कई lists में ऊँची rank वाले items को final list में भी ऊँची rank मिले
- साथ ही, जो items किसी एक list में बहुत ऊपर हैं लेकिन दूसरी में बहुत नीचे, उन्हें final list में अनावश्यक रूप से ऊँची rank न मिले
- score को denominator में rank रखकर calculate करने से low-rank records को penalty दी जा सकती है
- ध्यान देने योग्य बातें
$rrf_k: पहले rank वाले item का score बहुत ज्यादा extreme न हो जाए (क्योंकि rank से divide किया जाता है), इसके लिए denominator में एक constant k जोड़ा जाता है ताकि scores smooth हो सकें
$_weight: हर method को अलग weight दिया जा सकता है। results tune करने में यह बहुत उपयोगी है
fuzzy search implement करना
- ऊपर के तरीकों से बहुत कुछ संभाला जा सकता है, लेकिन named entities में typo हो तो तुरंत समस्या आ सकती है
- semantic search similarity पकड़कर इनमें से कुछ समस्याएँ कम कर सकती है, लेकिन names, abbreviations, और दूसरे ऐसे text के लिए यह संघर्ष करती है जो semantically similar नहीं होते
- इसे कम करने के लिए
pg_trgm extension जोड़ा जाता है ताकि fuzzy search संभव हो सके
- यह trigrams पर काम करता है। trigram किसी शब्द को 3-character sequences में तोड़ता है, इसलिए fuzzy search में उपयोगी है
- इससे typos या हल्के variations होने पर भी मिलते-जुलते शब्द match किए जा सकते हैं
- उदाहरण के लिए, "hello" और "helo" कई trigrams share करते हैं, इसलिए fuzzy search में उन्हें match करना आसान हो सकता है
- इच्छित column पर नया index बनाकर उसे पूरे search query में जोड़ा जाता है
pg_trgm extension % operator उपलब्ध कराता है, जो similarity pg_trgm.similarity_threshold (default 0.3) से अधिक होने पर text को filter करता है
- इसके अलावा कई और उपयोगी operators भी हैं
full-text search tuning
tsvector weights को adjust करना: असली documents में सिर्फ title ही नहीं, content भी होता है
- कई columns होने पर भी embedding column एक ही रखा जाता है
- व्यक्तिगत रूप से, कई embeddings रखने की तुलना में
title और body को एक ही embedding में रखने से performance में बड़ा अंतर नहीं पाया गया
- आखिरकार,
title को body का संक्षिप्त प्रतिनिधित्व होना चाहिए। ज़रूरत के अनुसार इसका प्रयोग करके देखना अच्छा है
title छोटा और keyword-rich होने की उम्मीद होती है, जबकि body लंबा और अधिक विवरण वाला होता है
- इसलिए यह tune करना चाहिए कि full-text search columns एक-दूसरे के मुकाबले कैसे weight किए जाएँ
- document में किसी शब्द की position या importance के आधार पर priority दी जा सकती है
- A-weight: सबसे महत्वपूर्ण (जैसे title, header)। default
1.0
- B-weight: महत्वपूर्ण (जैसे document की शुरुआत, summary)। default
0.4
- C-weight: मानक महत्व (जैसे body text)। default
0.2
- D-weight: सबसे कम महत्वपूर्ण (जैसे footnotes, annotations)। default
0.1
- document structure और application requirements के अनुसार weights adjust करके relevance को fine-tune किया जा सकता है
- title को ज्यादा weight देने के कारण
- title आम तौर पर document के मुख्य विषय को संक्षेप में व्यक्त करता है
- users search करते समय पहले titles को स्कैन करते हैं, इसलिए title के keyword matches अक्सर body text के matches की तुलना में user intent से अधिक जुड़े होते हैं
लंबाई के आधार पर adjustment
ts_rank_cd docs पढ़ने पर पता चलता है कि इसमें normalization parameter होता है
- > दोनों ranking functions एक integer
normalization option का उपयोग करते हैं, जो यह तय करता है कि document की लंबाई ranking को कैसे प्रभावित करेगी। यह integer option एक bit mask है क्योंकि यह कई behaviors को नियंत्रित करता है: | का उपयोग करके एक या अधिक behaviors निर्दिष्ट किए जा सकते हैं (उदाहरण: 2|4)
- इन अलग-अलग options का उपयोग करके आप यह कर सकते हैं
- document length bias को adjust करना
- अलग-अलग document sets में relevance balance करना
- consistent representation के लिए ranking results को adjust करना
- title के लिए
0 (कोई normalization नहीं), और body के लिए 1 (log document length) सेट करने से अच्छे results मिल सकते हैं
- फिर भी, अपने use case के लिए सबसे उपयुक्त option खोजने के लिए अलग-अलग options के साथ प्रयोग करना बेहतर है
cross-encoder के साथ reranking
- कई search systems दो चरणों में काम करते हैं
- यानी, पहले bi-encoder से शुरुआती N results retrieve किए जाते हैं, फिर cross-encoder से उन results की search query के साथ तुलना करके ranking की जाती है
- bi-encoder: तेज होता है, इसलिए बड़ी संख्या में documents retrieve करने के लिए अच्छा है
- cross-encoder
- धीमा है, लेकिन performance बेहतर है, इसलिए retrieved results को rerank करने के लिए अच्छा है
- query और document को साथ में process करता है, जिससे दोनों के बीच संबंध को अधिक सूक्ष्मता से समझा जा सकता है
- यह बेहतर ranking accuracy देता है, लेकिन इसकी कीमत compute time और scalability में चुकानी पड़ती है
- इसके लिए कई तरह के tools उपलब्ध हैं
- सबसे अच्छे विकल्पों में से एक Cohere का Rerank है
- दूसरा तरीका है OpenAI के GPT का उपयोग करके इसे खुद बनाना
- cross-encoder query और document के बीच संबंध को बेहतर समझकर search results की accuracy बढ़ा सकता है
- लेकिन compute cost ज्यादा होने के कारण scalability के मामले में इसकी सीमाएँ हैं
- इसलिए दो-चरणीय approach प्रभावी है: शुरुआती retrieval के लिए bi-encoder, और केवल retrieve किए गए कुछ documents पर cross-encoder लागू करना
कब alternative solution तलाशना चाहिए
- PostgreSQL कई search scenarios के लिए उपयुक्त विकल्प है, लेकिन इसकी सीमाएँ भी हैं
- BM25 जैसे advanced algorithms की अनुपस्थिति अलग-अलग document lengths को handle करते समय महसूस हो सकती है
- PostgreSQL का full-text search TF-IDF पर निर्भर करता है, इसलिए बहुत लंबे documents और बड़े collections में rare terms के साथ यह संघर्ष कर सकता है
- alternative solution ढूँढ़ने से पहले measurement ज़रूर करना चाहिए। हो सकता है इसकी ज़रूरत ही न पड़े
निष्कर्ष
- इस लेख में basic full-text search से लेकर fuzzy matching, semantic search, और result boosting जैसी advanced techniques तक काफी कुछ शामिल है
- Postgres की शक्तिशाली capabilities का उपयोग करके आप अपनी ज़रूरतों के अनुसार एक मजबूत और लचीला search engine बना सकते हैं
- search के लिए Postgres शायद पहला tool न हो जो दिमाग में आए, लेकिन यह आपको बहुत दूर तक ले जा सकता है
- शानदार search experience की कुंजी
- लगातार iteration और fine-tuning
- चर्चा की गई debugging techniques का उपयोग करके search performance को समझें, और user feedback व behavior के आधार पर weights और parameters adjust करने से न डरें
- PostgreSQL में कुछ advanced search features की कमी हो सकती है, लेकिन ज्यादातर मामलों में यह पर्याप्त रूप से शक्तिशाली search engine बनाने के लिए काफी है
- alternative solution देखने से पहले Postgres की capabilities का पूरा उपयोग करना और performance को मापना बेहतर है; अगर तब भी कमी लगे, तभी किसी दूसरे solution पर विचार किया जा सकता है
2 टिप्पणियां
यह जानने की जिज्ञासा है कि क्या Korean सर्च भी अच्छी तरह काम करता है।
आज का वीकली विषय भी Postgres था, और जैसा उम्मीद थी, यह फिर से Postgres ही है। लगता है इसकी लोकप्रियता के अनुपात में इस पर काफी लेख आ रहे हैं, हाहा।
BM25 के लिए नीचे देखें.
pg_bm25 - Postgres में Elastic स्तर की गुणवत्ता देने वाला Full-Text सर्च एक्सटेंशन
ParadeDB - PostgreSQL for Search