আমরা ১-২ সপ্তাহে Upstash Redis Search লঞ্চ করব, কিন্তু আমি কিছু অর্লি থটস শেয়ার করতে চাই আমরা কী বানাচ্ছি এবং কেন আমি এক্সাইটেড।
আমরা কেন এটি বানাচ্ছি
আমরা ২০২৪ থেকে সার্চ স্পেসে আছি, Upstash Vector দিয়ে শুরু করলাম। Vector লোকেদার সেমান্টিক সার্চ ইমপ্লিমেন্ট করতে দিয়েছিল, এবং পরে আমরা এই প্রোডাক্ট স্পেসে ডাবল ডাউন করলাম Upstash Search দিয়ে।
উদাহরণস্বরূপ, এটা আমাদের ২০২৫ সালের লঞ্চ টুইট Upstash Search ঘোষণা করার, আমাদের ভেক্টর-বেসড সেমান্টিক সার্চ সমাধান 👇

আমি মনে করি আমরা আরও ভালো করতে পারি এবং Search থেকে আমাদের লার্নিংসের ওপর বিল্ড আপ করতে পারি।
আমি অধিকাংশ এক্সিস্টিং সার্চ প্রোভাইডারের সাথে খুব অনহ্যাপি। আমার ব্যক্তিগত মতামতে, কেউ সার্ভারলেস স্পেসে ভালো ফিট করে না। এটাই কারণ আমি ২০২৩-এ নিজের সার্চ অ্যাপ বানিয়েছিলাম AWS Cloudsearch দিয়ে 💀
আমরা যা চাইছিলাম সেটা:
- Redis-এ থাকে কারণ Redis ফাস্ট af
- Upstash Redis SDK-র সাথে কাজ করে
- ১০০% টাইপ-সেফ
- রিয়েল-টাইম সার্চ-অ্যাজ-ইউ-টাইপের জন্য পর্যাপ্ত দ্রুত
তাই আমরা এটা বানাচ্ছি।
Redis API-এর বাইরে আমাদের প্রথম এক্সটেনশন
এটা আমাদের জন্য বড় ব্যাপার। এখন পর্যন্ত, @upstash/redis রেডিস কমান্ডের নিকট ১:১ ম্যাপিং ছিল। Search সেটা থেকে বাইরে আমাদের প্রথম এক্সটেনশন।
আমরা Tantivy ব্যবহার করছি হুডের নিচে, একটি ফুল-টেক্সট সার্চ ইঞ্জিন রাস্ট-এ লেখা যা Apache Lucene-র অনুপ্রাণিত। এটা ফাস্ট। খুব ফাস্ট। এবং এটা আমাদের সব প্রিমিটিভ দিয়ে দেয় যেন টোকেনাইজেশন, স্টেমিং, ফাজি ম্যাচিং, ফ্রেজ কোয়েরি, এবং BM25 স্কোরিং।
লক্ষ্য হলো এটাকে SDK এবং Upstash Redis-এ নেটিভ ফিল করানো। আপনি আজ @upstash/redis ব্যবহার করছেন তবে, সার্চ যোগ করা যেন একটি প্রাকৃতিক এক্সটেনশন লাগে এবং না যেন একটি আলাদা প্রোডাক্ট।
টাইপ-সেফ স্কিমা বিল্ডার
একটা জিনিস যেটা আমি খুব খুশি, সেটা হলো নতুন স্কিমা বিল্ডার। আমরা আমাদের সার্চেবল ফিল্ড ডিফাইন করি একটি zod-লাইক 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() মেথডগুলো আমাদের কন্ট্রোল দেয় টেক্সট কীভাবে প্রসেস হবে সেটা ওপর:
- Tokenization টেক্সটকে সার্চেবল ওয়ার্ডে ভাগ করে। ন্যাচারাল ল্যাঙ্গুয়েজের জন্য চমৎকার, কিন্তু SKU-এর মত জিনিস ভাঙে (
SKU-12345-BLKহয়ে যায়["SKU", "12345", "BLK"])। কোড, ইমেইল, এবং UUID-এর জন্য ডিজেবল করুন। - Stemming ওয়ার্ডগুলকে তাদের রুট ফর্মে রিডিউস করে যাতে "running" "run"-এর সাথে ম্যাচ করে। ব্র্যান্ড নাম এবং প্রপার নাউনের জন্য ডিজেবল করুন যেখানে এক্স্যাক্ট ম্যাচিং চান।
স্কিমাটা কোয়েরিতে ফুল টাইপ ইনফারেন্স দেয়। আপনি যদি এমন ফিল্ড কোয়েরি করার চেষ্টা করেন যা এক্সিস্ট করে না, TypeScript সেটা ক্যাচ করবে। আমরা স্কিমা সিনট্যাক্সকে খুব নিকট রাখছি zod সিনট্যাক্সের যাতে এটি ফ্যামিলিয়ার লাগে ব্যবহার করতে।
কোয়েরি প্রিমিটিভস
আমরা পাঁচটি মূল অপারেটর দিয়ে লঞ্চ করছি যা আমরা মনে করি অধিকাংশ সার্চ ইউজ কেস কভার করে:
$smart স্মার্ট ম্যাচিং-এর জন্য
$smart অপারেটর দিয়ে আমরা অটোমেটিক্যালি স্মার্ট ম্যাচিং অ্যাপ্লাই করি। এই অপারেটর যেন জাস্ট ওয়ার্ক™ এবং বেগিনারদের জন্য সেরা উপায় হয় শুরু করার।
await products.query({
filter: {
name: { $smart: "wirless headphones" },
},
});
হুডের নিচে, এটি রান করে:
- এক্স্যাক্ট ফ্রেজ ম্যাচ (হাইএস্ট বুস্ট) - যেসব ডকুমেন্টে "wireless headphones" অ্যাডজাসেন্ট এবং ইন অর্ডার
- ফ্রেজ উইথ স্লপ (মিডিয়াম বুস্ট) - যেসব ডকুমেন্টে ওয়ার্ডগুলো অর্ডারে আছে কিন্তু অ্যাডজাসেন্ট নয় (যেন wireless bose headphones)
- টার্মস ম্যাচ (মিডিয়াম বুস্ট) - যেসব ডকুমেন্টে সব টার্ম আছে, যেকোনো অর্ডারে
- ফাজি ম্যাচিং (নো বুস্ট) - টাইপোवाले ডকুমেন্ট যেন "wireles headphone"
- ফাজি প্রিফিক্স অন লাস্ট ওয়ার্ড (নো বুস্ট) - সার্চ-অ্যাজ-ইউ-টাইপ সিনেরিওর জন্য
স্কোরগুলো কম্বাইন হয় সবচেয়ে রিলেভান্ট রেজাল্ট পেতে। অধিকাংশ সার্চ বক্সের জন্য, এইটাই আপনার প্রয়োজনীয় সব। অবশ্যই আপনি এই অপারেটর নিজে ইমপ্লিমেন্ট করতে পারেন এবং সেটিংস নিয়ে এক্সপেরিমেন্ট করতে পারেন, কারণ এটি নিচের প্রিমিটিভ অপারেটরগুলোর ওপর বিল্ট।
$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 টাইপো টলারেন্সের জন্য
কনফিগারেবল টাইপো টলারেন্স (যেন ২ টাইপো) সহ ফাজি ম্যাচিং:
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" } },
],
},
});
এটি নেম ম্যাচকে ডেসক্রিপশন ম্যাচের চেয়ে দ্বিগুণ মূল্যবান করে। এটি উপযোগী যখন আমরা টাইটেল ম্যাচকে বডি ম্যাচের ওপর র্যাঙ্ক করতে চাই।
আগামী কী
এই আর্টিকেলে যা যা দিয়েছি সেগুলো এখনও চেঞ্জ হতে পারে। আমরা এখনো এজ ধার ধোয়া এবং ডক্স লিখছি। অফিসিয়াল লঞ্চ ১-২ সপ্তাহে।
তবুও আমি মনে করি একসাথে ফার্স্ট লুক নেওয়া খুব কুল 👀
লঞ্চের পর আমরা কিছু এক্সপ্লোর করতে পারি:
- ভেক্টর সার্চ ইন্টিগ্রেশন (সেমান্টিক + কিয়ওয়ার্ড হাইব্রিড সার্চ)
- অটোকমপ্লিট এবং সাজেশন
আপনি যদি অর্লি অ্যাক্সেস চান বা প্রশ্ন থাকে, আমার সাথে @joshtriedcoding-এ যোগাযোগ করুন।
পড়ার জন্য ধন্যবাদ 🙌