আমরা আগামী 1-2 সপ্তাহের মধ্যে Upstash Redis সার্চ চালু করছি, কিন্তু আমরা কী তৈরি করছি এবং কেন আমি এটা নিয়ে উত্তেজিত সেই বিষয়ে কিছু প্রাথমিক চিন্তাভাবনা শেয়ার করতে চেয়েছিলাম।
কেন আমরা এটি তৈরি করছি
আমরা 2024 সাল থেকে অনুসন্ধানের জায়গায় আছি, Upstash ভেক্টর দিয়ে শুরু করে। ভেক্টর লোকেদের শব্দার্থিক অনুসন্ধান বাস্তবায়নের অনুমতি দেয়, এবং তারপরে আমরা Upstash অনুসন্ধানের সাথে এই পণ্যের জায়গায় দ্বিগুণ নেমে এসেছি।
উদাহরণস্বরূপ, এটি 2025 থেকে আমাদের লঞ্চ করা টুইট যা Upstash সার্চ ঘোষণা করে, আমাদের ভেক্টর-ভিত্তিক শব্দার্থিক অনুসন্ধান সমাধান 👇

এবং আমি মনে করি আমরা আরও ভাল করতে পারি এবং অনুসন্ধান থেকে আমাদের শিক্ষার উপর ভিত্তি করে গড়ে তুলতে পারি।
আমরা বেশিরভাগ বিদ্যমান অনুসন্ধান প্রদানকারীর সাথে বেশ অসন্তুষ্ট। ব্যক্তিগতভাবে আমার কাছে, তাদের কেউই সার্ভারহীন স্থানের সাথে ভালভাবে ফিট করে না। এতটাই যে আমি 2023 সালে AWS ক্লাউডসার্চের উপর ভিত্তি করে আমার নিজস্ব অনুসন্ধান অ্যাপ তৈরি করেছি 💀
আমরা যা চেয়েছিলাম তা ছিল এমন কিছু:
- রেডিসে থাকে কারণ রেডিস দ্রুত হয়
- Upstash Redis SDK-এর সাথে কাজ করে
- 100% টাইপ-নিরাপদ
- রিয়েল-টাইম অনুসন্ধান-যেমন-আপনি-টাইপ করার জন্য যথেষ্ট দ্রুত
তাই আমরা এটি নির্মাণ করছি।
Redis API এর বাইরে আমাদের প্রথম এক্সটেনশন
এটা আমাদের জন্য অনেক বড় ব্যাপার। এখন পর্যন্ত, @upstash/redis রেডিস কমান্ডের প্রায় 1:1 ম্যাপিং হয়েছে। এর বাইরে অনুসন্ধান আমাদের প্রথম এক্সটেনশন৷
আমরা হুডের নীচে Tantivy ব্যবহার করছি, একটি সম্পূর্ণ-টেক্সট সার্চ ইঞ্জিন যা রাস্টে লেখা যা Apache Lucene দ্বারা অনুপ্রাণিত। এটা দ্রুত। সত্যিই দ্রুত. এবং এটি আমাদেরকে টোকেনাইজেশন, স্টেমিং, ফাজি ম্যাচিং, ফ্রেস কোয়েরি এবং BM25 স্কোরিংয়ের মতো প্রয়োজনীয় সমস্ত আদিম জিনিস দেয়৷
লক্ষ্য SDK এবং Upstash Redis নিজেই এই অনুভূতি নেটিভ করা. আপনি যদি @upstash/redis ব্যবহার করেন আজ, অনুসন্ধান যোগ করা একটি প্রাকৃতিক এক্সটেনশনের মতো অনুভব করা উচিত এবং একটি পৃথক পণ্য নয়৷
টাইপ-সেফ স্কিমা নির্মাতা
নতুন স্কিমা নির্মাতার সাথে আমি সত্যিই খুশি। আমরা আমাদের অনুসন্ধানযোগ্য ক্ষেত্রগুলিকে জোড-এর মতো API দিয়ে সংজ্ঞায়িত করি:
import { Redis, s } from "@upstash/redis";
const redis = Redis.fromEnv();
const schema = s.object({
name: s.string(),
description: s.string(),
sku: s.string().noTokenize(),
brand: s.string().noStem(),
price: s.number(),
inStock: s.boolean(),
});
const products = await redis.search.createIndex({
name: "products",
dataType: "json",
prefix: "product:",
schema,
});
.noTokenize() এবং .noStem() পদ্ধতিগুলি আমাদের নিয়ন্ত্রণ করতে দেয় কীভাবে পাঠ্য প্রক্রিয়া করা হয়:
- টোকেনাইজেশন পাঠ্যকে অনুসন্ধানযোগ্য শব্দে ভাগ করে। প্রাকৃতিক ভাষার জন্য দুর্দান্ত, তবে SKUs (
SKU-12345-BLK) এর মতো জিনিসগুলি ভেঙে দেয়["SKU", "12345", "BLK"]হয়ে যায় ) কোড, ইমেল এবং UUID এর জন্য এটি নিষ্ক্রিয় করুন। - স্টেমিং শব্দগুলিকে তাদের মূল আকারে কমিয়ে দেয় তাই "দৌড়" মেলে "রান"। ব্র্যান্ড নাম এবং সঠিক বিশেষ্যের জন্য এটি নিষ্ক্রিয় করুন যেখানে আমরা সঠিক মিল চাই৷
স্কিমা আমাদের প্রশ্নের সম্পূর্ণ ধরনের অনুমান দেয়। যদি আমরা এমন একটি ক্ষেত্র অনুসন্ধান করার চেষ্টা করি যা বিদ্যমান নেই, টাইপস্ক্রিপ্ট এটি ধরবে। আমরা স্কিমা সিনট্যাক্সকে জোড সিনট্যাক্সের খুব কাছাকাছি রাখছি যাতে এটি ব্যবহার করা পরিচিত মনে হয়।
কোয়েরি আদিম
আমরা পাঁচটি প্রধান অপারেটরের সাথে চালু করছি যা আমরা মনে করি বেশিরভাগ অনুসন্ধান ব্যবহারের ক্ষেত্রে কভার করে:
$smart স্মার্ট ম্যাচিংয়ের জন্য
$smart অপারেটরের সাথে আমরা স্বয়ংক্রিয়ভাবে স্মার্ট ম্যাচিং প্রয়োগ করি। এই অপারেটরকে শুধু কাজ করা উচিত™ এবং নতুনদের জন্য শুরু করার সর্বোত্তম উপায় হওয়া উচিত৷
৷await products.query({
filter: {
name: { $smart: "wirless headphones" },
},
}); হুডের নিচে, এটি চলে:
- সঠিক বাক্যাংশ মিল (সর্বোচ্চ বুস্ট) - "ওয়্যারলেস হেডফোন" সংলগ্ন এবং ক্রমানুসারে ডকুমেন্টস
- স্লপ সহ বাক্যাংশ (মাঝারি বুস্ট) - নথি যেখানে শব্দগুলি ক্রমানুসারে প্রদর্শিত হয় কিন্তু সংলগ্ন নয় (যেমন ওয়্যারলেস বোস হেডফোন)
- শর্ত মেলে (মাঝারি বুস্ট) - সমস্ত শর্তাবলী, যেকোনো আদেশ সম্বলিত নথি
- অস্পষ্ট মিল (কোন বুস্ট নেই) - "ওয়্যারলস হেডফোন" এর মতো টাইপো সহ নথি৷
- শেষ শব্দের অস্পষ্ট উপসর্গ (কোন বুস্ট নয়) - সার্চ-যেমন-আপনি-টাইপ পরিস্থিতির জন্য
সর্বাধিক প্রাসঙ্গিক ফলাফল পেতে স্কোরগুলি একত্রিত করা হয়। বেশিরভাগ অনুসন্ধান বাক্সের জন্য, এটি আক্ষরিক অর্থেই আপনার প্রয়োজন। অবশ্যই আপনি নিজেই এই অপারেটরটি প্রয়োগ করতে পারেন এবং সেটিংসের সাথে খেলতে পারেন, কারণ এটি নীচের অন্যান্য আদিম অপারেটরগুলিতে নির্মিত৷
$eq সঠিক সমতার জন্য
ক্ষেত্রগুলির জন্য যেখানে আমরা সঠিক মিল চাই:
await products.query({
filter: {
name: { $eq: "wireless headphones" },
price: { $eq: 200 },
},
}); $phrase শব্দগুচ্ছ মিলের জন্য
যখন আমাদের সংলগ্ন এবং ক্রমানুসারে উপস্থিত হওয়ার জন্য শব্দের প্রয়োজন হয়:
await products.query({
filter: { description: { $phrase: "noise cancelling" } },
});
আমরা slop যোগ করতে পারি এর মধ্যে শব্দের অনুমতি দিতে:
await products.query({
filter: {
description: {
$phrase: { value: "wireless headphones", slop: 2 },
},
},
}); $fuzzy টাইপো সহনশীলতার জন্য
কনফিগারযোগ্য টাইপো সহনশীলতার সাথে অস্পষ্ট মিলের জন্য (যেমন 2টি টাইপো):
await products.query({
filter: { name: { $fuzzy: "headphonse", distance: 2 } },
}); $regex প্যাটার্ন মেলার জন্য
যখন আমাদের নিয়মিত এক্সপ্রেশন প্যাটার্নের প্রয়োজন হয়:
await products.query({
filter: { sku: { $regex: "SKU-[0-9]{5}-.*" } },
});
একটি জিনিস নোট করুন:regex .noStem() সহ ক্ষেত্রগুলিতে সবচেয়ে ভাল কাজ করে যেহেতু স্টেমড টেক্সট প্রত্যাশিত প্যাটার্নের সাথে মেলে না।
নির্দিষ্ট ক্ষেত্র বুস্ট করা
আমরা উচ্চতর কিছু ম্যাচের ওজন বাড়াতে প্রয়োগ করতে পারি:
await products.query({
filter: {
$and: [
{ name: { $smart: "wireless", $boost: 2 } },
{ description: { $smart: "wireless" } },
],
},
}); এটি বর্ণনার মিলের তুলনায় নামের মিলকে দ্বিগুণ মূল্যবান করে তোলে। যখন আমরা শিরোনাম ম্যাচগুলিকে বডি ম্যাচের উপরে স্থান দিতে চাই তখন এটি কার্যকর।
এরপর কি হবে
আমি এই নিবন্ধে রাখা সমস্ত জিনিস এখনও পরিবর্তনের জন্য উন্মুক্ত. আমরা এখনও প্রান্ত পালিশ করছি এবং ডক্স লিখছি। অফিসিয়াল লঞ্চ 1-2 সপ্তাহের মধ্যে৷
৷কিন্তু আমি মনে করি এটা সত্যিই চমৎকার যে আমরা একসাথে প্রথমবার দেখতে পারি 👀
লঞ্চের পরে আমরা কিছু জিনিস অন্বেষণ করতে পারি:
- ভেক্টর অনুসন্ধান ইন্টিগ্রেশন (অর্থবোধক + কীওয়ার্ড হাইব্রিড অনুসন্ধান)
- স্বয়ংসম্পূর্ণ এবং পরামর্শ
আপনি যদি তাড়াতাড়ি অ্যাক্সেস চান বা প্রশ্ন থাকে, আমার সাথে যোগাযোগ করুন @joshtriedcoding.
পড়ার জন্য ধন্যবাদ 🙌