paint-brush
WebAssembly, Rust और Xor फ़िल्टर के साथ Next.js में स्टैटिक फ़ुल-टेक्स्ट सर्चद्वारा@dawchihliou
7,395 रीडिंग
7,395 रीडिंग

WebAssembly, Rust और Xor फ़िल्टर के साथ Next.js में स्टैटिक फ़ुल-टेक्स्ट सर्च

द्वारा Daw-Chih Liou2022/04/08
Read on Terminal Reader
Read this story w/o Javascript

बहुत लंबा; पढ़ने के लिए

 रस्ट के साथ WebAssembly विकसित करने के लिए एक समृद्ध टूलकिट है। मजा आता है! 🤝 WebAssembly और Next.js एक साथ काफी अच्छा खेलते हैं, लेकिन ज्ञात मुद्दों से अवगत रहें। ‍🔬 Xor फ़िल्टर डेटा संरचनाएँ हैं जो महान मेमोरी दक्षता और मूल्य अस्तित्व के लिए तेज़ लुकअप प्रदान करती हैं। ‍🍳 WebAssembly के प्रदर्शन और कोड आकार की गारंटी नहीं है। माप और बेंचमार्क करना सुनिश्चित करें।

Companies Mentioned

Mention Thumbnail
Mention Thumbnail

Coin Mentioned

Mention Thumbnail
featured image - WebAssembly, Rust और Xor फ़िल्टर के साथ Next.js में स्टैटिक फ़ुल-टेक्स्ट सर्च
Daw-Chih Liou HackerNoon profile picture


टीएल; डीआर

  • रस्ट के साथ WebAssembly विकसित करने के लिए एक समृद्ध टूलकिट है। मजा आता है!
  • 🤝 WebAssembly और Next.js एक साथ काफी अच्छा खेलते हैं, लेकिन ज्ञात मुद्दों से अवगत रहें।
  • ‍🔬 Xor फ़िल्टर डेटा संरचनाएं हैं जो महान मेमोरी दक्षता और मूल्य अस्तित्व के लिए तेज़ लुकअप प्रदान करती हैं।
  • ‍🍳 WebAssembly के प्रदर्शन और कोड आकार की गारंटी नहीं है। माप और बेंचमार्क करना सुनिश्चित करें।

मुझे हमेशा से पता था कि मैं अपने पोर्टफोलियो में लेखों के लिए एक पूर्ण-पाठ खोज सुविधा चाहता हूं ताकि आगंतुकों को उस सामग्री तक त्वरित पहुंच प्रदान की जा सके जिसमें वे रुचि रखते हैं। कंटेंटलेयर में माइग्रेट करने के बाद, यह अब दूर की कौड़ी नहीं लगती। तो मैंने एक्सप्लोर करना शुरू किया🚀

tinysearch से प्रेरित : एक WebAssembly पूर्ण-पाठ खोज इंजन

कुछ शोध के बाद, मुझे tinysearch नामक एक खोज इंजन मिला। यह एक स्थिर खोज इंजन है जिसे Rust और WebAssembly (Wasm) के साथ बनाया गया है। लेखक मैथियस tinysearch ने एक अद्भुत ब्लॉग पोस्ट लिखा है कि कैसे छोटे खोज के बारे में आया।


मुझे निर्माण के समय एक न्यूनतर खोज इंजन बनाने और ब्राउज़र को अनुकूलित निम्न-स्तरीय कोड में शिपिंग करने का विचार पसंद आया। इसलिए मैंने tinysearch को ब्लूप्रिंट के रूप में उपयोग करने और अपनी Next.js स्थिर साइट के साथ एकीकृत करने के लिए अपना स्वयं का खोज इंजन लिखने का निर्णय लिया।


मैं tinysearch के कोडबेस को पढ़ने की अत्यधिक अनुशंसा करता हूं। बहुत अच्छा लिखा है। मेरे खोज इंजन का कार्यान्वयन इसका एक सरलीकृत संस्करण है। मूल तर्क वही है।

खोज सुविधा कैसी दिखती है?

बहुत आसान:

  • उपयोगकर्ता खोज इनपुट में कुछ भी टाइप करते हैं।
  • खोज इंजन सभी सामग्री में खोजशब्दों की खोज करता है और सबसे अधिक प्रासंगिक लेख पाता है।
  • UI एक रैंक की गई खोज परिणाम सूची प्रदर्शित करता है।


आप लेख पृष्ठ पर खोज फ़ंक्शन को आज़मा सकते हैं!

आँकड़ों का एक छोटा सा

इस लेख को लिखते समय, ये हैं:

  • 7 लेख (आने के लिए और अधिक🚀)
  • 13,925 शब्द
  • 682KB डेटा फ़ाइलें (Contentlayer द्वारा जेनरेट की गई)


पूर्ण-पाठ खोज के लिए गति के लिए भड़काने वाली स्थिर साइटों के लिए काम करने के लिए, कोड का आकार छोटा होना चाहिए।

WebAssembly पूर्ण-पाठ खोज फ़ंक्शन कैसे कार्य करता है?

अधिकांश आधुनिक ब्राउज़र अब WebAssembly का समर्थन करते हैं । वे जावास्क्रिप्ट के साथ-साथ देशी WebAssembly कोड और बाइनरी चलाने में सक्षम हैं।


खोज फ़ंक्शन की अवधारणा सीधी है। यह एक पैरामीटर के रूप में एक क्वेरी स्ट्रिंग में लेता है। फ़ंक्शन में, हम क्वेरी को खोज शब्दों में टोकन देते हैं। फिर हम प्रत्येक लेख को इस आधार पर एक रैंकिंग स्कोर देते हैं कि उसमें कितने खोज शब्द हैं। अंत में, हम प्रासंगिकता के आधार पर लेखों को रैंक करते हैं। स्कोर जितना अधिक होगा, उतना ही प्रासंगिक होगा।


प्रवाह इस तरह दिखता है:


लेखों को स्कोर करना वह जगह है जहाँ सबसे अधिक कंप्यूटिंग आती है। एक सरल दृष्टिकोण प्रत्येक लेख को एक हैश सेट में बदलना होगा जिसमें लेख के सभी अद्वितीय शब्द शामिल हों। हैश सेट में कितने खोज शब्द हैं, इसकी गणना करके हम स्कोर की गणना कर सकते हैं।


आप कल्पना कर सकते हैं कि हैश सेट के साथ यह सबसे अधिक मेमोरी-कुशल दृष्टिकोण नहीं है। इसे बदलने के लिए बेहतर डेटा संरचनाएं हैं: xor फ़िल्टर

Xor फ़िल्टर क्या हैं?

Xor फ़िल्टर अपेक्षाकृत नई डेटा संरचनाएं हैं जो हमें यह अनुमान लगाने की अनुमति देती हैं कि कोई मान मौजूद है या नहीं। यह तेज़ और स्मृति-कुशल है इसलिए यह पूर्ण-पाठ खोज के लिए बहुत उपयुक्त है।


हैश सेट जैसे वास्तविक इनपुट मानों को संग्रहीत करने के बजाय, xor फ़िल्टर एक विशिष्ट तरीके से इनपुट मानों के फ़िंगरप्रिंट (L-बिट हैश अनुक्रम) को संग्रहीत करता है। यह देखते हुए कि क्या फ़िल्टर में कोई मान मौजूद है, यह जाँचता है कि मान का फ़िंगरप्रिंट मौजूद है या नहीं।


हालाँकि, Xor फ़िल्टर में कुछ ट्रेड-ऑफ़ हैं:

  • Xor फ़िल्टर संभाव्य हैं और एक मौका है कि गलत-सकारात्मक हो सकता है।
  • Xor फ़िल्टर आंशिक मानों के अस्तित्व का अनुमान लगाने में सक्षम नहीं हैं। तो मेरे उपयोग के मामले में, पूर्ण-पाठ खोज केवल पूर्ण शब्दों की खोज करने में सक्षम होगी।

मैंने रस्ट के साथ Xor फ़िल्टर कैसे बनाए?

चूंकि मेरे पास कंटेंटलेयर द्वारा उत्पन्न आलेख डेटा था, इसलिए मैंने WebAssembly के निर्माण से पहले डेटा के साथ उन्हें खिलाकर xor फ़िल्टर का निर्माण किया। मैंने फिर xor फ़िल्टर को क्रमबद्ध किया और उन्हें एक फ़ाइल में संग्रहीत किया। WebAssembly में फ़िल्टर का उपयोग करने के लिए, मुझे बस इतना करना था कि स्टोरेज फ़ाइल से पढ़ना और फ़िल्टर को deserialize करना था।


फ़िल्टर पीढ़ी प्रवाह इस तरह दिखता है:


xorf क्रेट xor फ़िल्टर कार्यान्वयन के लिए एक अच्छा विकल्प है क्योंकि यह क्रमांकन/डिसेरिएलाइज़ेशन और कुछ सुविधाएँ प्रदान करता है जो स्मृति दक्षता और झूठी-सकारात्मक दर में सुधार करती हैं। यह मेरे उपयोग के मामले में तारों के एक टुकड़े के साथ एक xor फ़िल्टर बनाने के लिए एक बहुत ही आसान HashProxy संरचना भी प्रदान करता है। रस्ट में लिखा गया निर्माण मोटे तौर पर इस तरह दिखता है:


 use std::collections::hash_map::DefaultHasher; use xorf::{Filter, HashProxy, Xor8}; mod utils; fn build_filter(title: String, body: String) -> HashProxy<String, DefaultHasher, Xor8> { let title_tokens: HashSet<String> = utils::tokenize(&title); let body_tokens: HashSet<String> = utils::tokenize(&body); let tokens: Vec<String> = body_tokens.union(&title_tokens).cloned().collect(); HashProxy::from(&tokens) }


यदि आप वास्तविक कार्यान्वयन में रुचि रखते हैं, तो आप रिपॉजिटरी में और अधिक पढ़ सकते हैं।

यह सब एक साथ डालें

WebAssembly को Next.js में एकीकृत करना

यहां बताया गया है कि मैंने xor फ़िल्टर जनरेशन स्क्रिप्ट और WebAssembly को Next.js के अंदर कैसे एकीकृत किया।

फ़ाइल संरचना इस तरह दिखती है:


 my-portfolio ├── next.config.js ├── pages ├── scripts │ └── fulltext-search ├── components │ └── Search.tsx └── wasm └── fulltext-search


WebAssembly का समर्थन करने के लिए, मैंने WebAssembly मॉड्यूल को async मॉड्यूल के रूप में लोड करने के लिए अपने वेबपैक कॉन्फ़िगरेशन को अपडेट किया। इसे स्थिर साइट निर्माण के लिए काम करने के लिए, मुझे .next/server निर्देशिका में WebAssembly मॉड्यूल जेनरेट करने के लिए वर्कअराउंड की आवश्यकता थी ताकि next build स्क्रिप्ट चलाते समय स्थिर पृष्ठ सफलतापूर्वक प्री-रेंडर कर सकें।


अगला.config.js

 webpack: function (config, { isServer }) { // it makes a WebAssembly modules async modules config.experiments = { asyncWebAssembly: true } // generate wasm module in ".next/server" for ssr & ssg if (isServer) { config.output.webassemblyModuleFilename = './../static/wasm/[modulehash].wasm' } else { config.output.webassemblyModuleFilename = 'static/wasm/[modulehash].wasm' } return config },


एकीकरण के लिए बस इतना ही है✨

प्रतिक्रिया घटक में WebAssembly का उपयोग करना

रस्ट कोड से WebAssembly मॉड्यूल बनाने के लिए, मैं wasm-pack का उपयोग करता हूं।

जावास्क्रिप्ट के लिए उत्पन्न .wasm फ़ाइल और ग्लू कोड wasm/fulltext-search/pkg में स्थित हैं। मुझे बस इतना करना था कि उन्हें गतिशील रूप से आयात करने के लिए next/dynamic का उपयोग करना था। ऐशे ही:


घटक/Search.tsx

 import React, { useState, useCallback, ChangeEvent, useEffect } from 'react' import dynamic from 'next/dynamic' type Title = string; type Url = string; type SearchResult = [Title, Url][]; const Search = dynamic({ loader: async () => { const wasm = await import('../../wasm/fulltext-search/pkg') return () => { const [term, setTerm] = useState('') const [results, setResults] = useState<SearchResult>([]) const onChange = useCallback((e: ChangeEvent<HTMLInputElement>) => { setTerm(e.target.value) }, []) useEffect(() => { const pending = wasm.search(term, 5) setResults(pending) }, [term]) return ( <div> <input value={term} onChange={onChange} placeholder="🔭 search..." /> {results.map(([title, url]) => ( <a key={url} href={url}>{title}</a> ))} </div> ) } }, }) export default Search

WebAssembly कोड आकार का अनुकूलन

बिना किसी अनुकूलन के, मूल Wasm फ़ाइल का आकार 114.56KB था। मैंने कोड आकार का पता लगाने के लिए ट्विगी का इस्तेमाल किया।


 Shallow Bytes │ Shallow % │ Item ───────────────┼───────────┼───────────────────── 117314 ┊ 100.00% ┊ Σ [1670 Total Rows]


कच्चे डेटा फ़ाइलों के 628KB की तुलना में, यह मेरी अपेक्षा से बहुत छोटा था। मैं इसे पहले से ही उत्पादन के लिए भेजकर खुश था लेकिन मैं यह देखने के लिए उत्सुक था कि मैं द रस्ट एंड वेबअसेंबली वर्किंग ग्रुप की अनुकूलन अनुशंसा के साथ कितना कोड आकार ट्रिम कर सकता हूं।


पहला प्रयोग एलटीओ को टॉगल कर रहा था और विभिन्न opt-level एस को आज़मा रहा था। निम्न कॉन्फ़िगरेशन सबसे छोटा .wasm कोड आकार देता है:


Cargo.toml

 [profile.release] + opt-level = 's' + lto = true
 Shallow Bytes │ Shallow % │ Item ───────────────┼───────────┼───────────────────── 111319 ┊ 100.00% ┊ Σ [1604 Total Rows]


इसके बाद, मैंने डिफ़ॉल्ट आवंटक को wee_alloc से बदल दिया।


wasm/fulltext-search/src/lib.rs

 + #[global_allocator] + static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;
 Shallow Bytes │ Shallow % │ Item ───────────────┼───────────┼───────────────────── 100483 ┊ 100.00% ┊ Σ [1625 Total Rows]


फिर मैंने Binaryen में wasm-opt टूल आज़माया।


 wasm-opt -Oz -o wasm/fulltext-search/pkg/fulltext_search_core_bg.wasm wasm/fulltext-search/pkg/fulltext_search_core_bg.wasm
 Shallow Bytes │ Shallow % │ Item ───────────────┼───────────┼───────────────────── 100390 ┊ 100.00% ┊ Σ [1625 Total Rows]


यह मूल कोड आकार से 14.4% की छूट है।


अंत में, मैं इसमें एक पूर्ण-पाठ खोज इंजन भेजने में सक्षम था:

  • 98.04 केबी कच्चा
  • 45.92 KB gzipped


बुरा नहीं😎

क्या यह वास्तव में तेज़ है?

मैंने वेब- web-sys के साथ प्रदर्शन की रूपरेखा तैयार की और कुछ डेटा एकत्र किया:

  • खोजों की संख्या: 208

  • मिनट: 0.046 एमएस

  • अधिकतम: 0.814 एमएस

  • माध्य: 0.0994 एमएस

  • मानक विचलन: 0.0678


एक पूर्ण-पाठ खोज करने में औसतन 0.1 एमएस से कम समय लगता है।


यह बहुत तेज़ है😎

अंतिम विचार

कुछ प्रयोगों के बाद, मैं WebAssembly, Rust और xor फ़िल्टर के साथ एक तेज़ और हल्के फुल-टेक्स्ट खोज का निर्माण करने में सक्षम था। यह Next.js और स्थिर साइट निर्माण के साथ अच्छी तरह से एकीकृत है।

गति और आकार कुछ ट्रेड-ऑफ के साथ आते हैं लेकिन उपयोगकर्ता अनुभव पर उनका बड़ा प्रभाव नहीं पड़ता है। यदि आप अधिक व्यापक खोज कार्यक्षमता की तलाश में हैं, तो यहां कुछ बेहतरीन उत्पाद उपलब्ध हैं:


सास सर्च इंजन

स्थिर खोज इंजन

सर्वर आधारित खोज इंजन

इन-ब्राउज़र खोज इंजन

संदर्भ


यह लेख डाव-चिह की वेबसाइट पर भी पोस्ट किया गया था।