मैंने OpenSearch और CJK के साथ Typo-Tolerant सर्च कैसे जोड़ा

शून्य-परिणाम वाली क्वेरीज़ हमारे वॉच टाइम को खत्म कर रही थीं।

एक साल तक, TopVideoHub सर्च के लिए SQLite FTS5 का उपयोग करता रहा। यह सटीक मिलान (exact matches) के लिए तो ठीक काम करता था, लेकिन जब यूज़र्स टाइपिंग में गलती (typos) करते थे, तो यह विफल हो जाता था।

लोग "demon slayer" के बजाय "demon slyer" सर्च करते थे। वे जापानी या कोरियाई शीर्षकों में अनावश्यक स्पेस (stray spaces) जोड़ देते थे। क्योंकि FTS5 सटीक टोकन (exact tokens) से मेल खाता है, इसलिए एक छोटी सी टाइपिंग गलती का मतलब था शून्य परिणाम। यूज़र्स दोबारा सर्च नहीं करते थे; वे बस चले जाते थे।

मैंने अपने टाइटल सर्च को OpenSearch पर स्थानांतरित कर दिया। यहाँ बताया गया है कि मैंने बहुभाषी (multilingual) दर्शकों के लिए इसे कैसे हल किया।

स्टैंडर्ड फज़ीनेस (Standard Fuzziness) के साथ समस्या

अधिकांश ट्यूटोरियल टाइपो को ठीक करने के लिए "fuzziness" का उपयोग करने की सलाह देते हैं। यह अंग्रेजी के लिए तो काम करता है, लेकिन चीनी, जापानी और कोरियाई (CJK) टेक्स्ट के लिए विफल हो जाता है।

  • CJK के लिए एडिट डिस्टेंस (Edit distance) खराब है। CJK में टाइपो का मतलब अक्सर पूरी तरह से गलत कैरेक्टर का उपयोग करना होता है।
  • स्टैंडर्ड फज़ीनेस से अर्थहीन परिणाम (semantic garbage) मिलते हैं। यह "fire" को "water" से मैच कर सकता है क्योंकि वे एक एडिट की दूरी पर हैं।
  • टोकनाइज़ेशन (Tokenization) कठिन है। CJK भाषाओं में शब्दों के बीच स्पेस का उपयोग नहीं किया जाता है।

समाधान: एक मल्टी-फील्ड अप्रोच (Multi-Field Approach)

मैंने सभी टेक्स्ट के साथ एक जैसा व्यवहार करना बंद कर दिया। मैंने एक लॉजिकल टाइटल फील्ड बनाया जो तीन अलग-अलग तरीकों से इंडेक्स करता है:

  • Latin और Romaji: मैंने fuzziness इनेबल करके स्टैंडर्ड टोकनाइज़ेशन का उपयोग किया। मैंने prefix length 1 सेट की। यह सुनिश्चित करता है कि "demon", "demn" से मैच हो जाए, लेकिन "lemon", "demon" से मैच न हो।
  • CJK टेक्स्ट: मैंने CJK bigram analyzer और एक ICU normalizer का उपयोग किया। मैंने fuzziness को OFF कर दिया। इसके बजाय, मैंने 70% का न्यूनतम मैच थ्रेशोल्ड (minimum match threshold) इस्तेमाल किया।
  • Autocomplete: मैंने search-as-you-type परिणामों के लिए edge-ngram फील्ड का उपयोग किया।

आर्किटेक्चर और डेटा सुरक्षा (Architecture and Data Safety)

मैंने SQLite को 'सिंगल सोर्स ऑफ ट्रुथ' (single source of truth) के रूप में रखा। OpenSearch एक तेज़ और दोबारा बनाया जा सकने वाला (rebuildable) इंडेक्स के रूप में कार्य करता है।

  • मैं OpenSearch में बल्क बैच में अपडेट भेजने के लिए PHP का उपयोग करता हूँ।
  • मैं यूज़र रिक्वेस्ट के दौरान कभी भी इंडेक्सिंग नहीं चलाता हूँ।
  • मैं "ड्रिफ्ट" (drift) की जाँच करने के लिए एक Python स्क्रिप्ट चलाता हूँ। यह सुनिश्चित करता है कि OpenSearch की गिनती SQLite की गिनती से मेल खाती है।

परिणाम

बदलाव बहुत बड़ा था:

  • शून्य-परिणाम वाली क्वेरीज़ 14% से घटकर 3% से भी कम हो गईं।
  • सर्च सेशन्स लंबे हो गए क्योंकि यूज़र्स को जो चाहिए था वह तुरंत मिल गया।
  • लेटेंसी (Latency) लगभग 40ms के आसपास कम बनी हुई है।

यदि आप बहुभाषी दर्शकों की सेवा करते हैं, तो याद रखें: टाइपो टॉलरेंस (typo tolerance) और CJK मैचिंग दो अलग-अलग समस्याएँ हैं। आपको दो अलग-अलग समाधानों की आवश्यकता है।

स्रोत: https://dev.to/ahmet_gedik778845/how-i-added-typo-tolerant-video-title-search-with-opensearch-and-cjk-3e5d