০৪ আগ, ২০২৫·6 মিনিট পড়তে

PostgreSQL সার্চ সর্বত্র: ফুল-টেক্সট, ট্রাইগ্রাম ও পার্শিয়াল ইনডেক্স

অভ্যন্তরীণ স্ক্রিনের জন্য PostgreSQL সার্চ ‘সার্চ-এভারিওয়্যার’ ডিজাইন করার কৌশল শিখুন—কখন ফুল-টেক্সট, ট্রাইগ্রাম ও পার্শিয়াল ইনডেক্স ব্যবহার করে দ্রুত রেজাল্ট পাবেন।

PostgreSQL সার্চ সর্বত্র: ফুল-টেক্সট, ট্রাইগ্রাম ও পার্শিয়াল ইনডেক্স

অভ্যন্তরীণ টুলে “সার্চ সর্বত্র” আসলে কী বোঝায়

একটি অভ্যন্তরীণ স্ক্রিনে, “সার্চ সর্বত্র” সাধারণত মানে: “আমি যে নির্দিষ্ট রেকর্ডটা ভাবছি, সেটা দ্রুত খুঁজে পেতে সাহায্য করুন, এমনকি যদি আমি সেটি পুরোটা মনে না রাখি।” মানুষ ব্রাউজ করছে না—তারা সরাসরি একজন কাস্টমার, টিকিট, ইনভয়েস বা ডিভাইসে চলে যেতে চায়।

এই কারণেই ধীর সার্চ ধীর পেজ লোডের চেয়ে বেশি খারাপ লাগে। একটি পেজ লোড একবার হয়; সার্চ একাধিকবার চালানো হয়, প্রায়ই কেউ কল চালিয়ে রাখলে বা ট্রায়াজ করার সময়। যদি রেজাল্ট 2–3 সেকেন্ড লাগে, ব্যবহারকারী কুয়েরি বদলে, ব্যাকস্পেস চাপিয়ে, অন্য টার্ম চেষ্টা করে—ফলে লোড ও হতাশা বাড়ে।

একটি সার্চ বক্স থেকে ব্যবহারকারীরা আশা করে একগুচ্ছ আচরণ: পার্শিয়াল ম্যাচ ("alex" খুঁজে "Alexander"), ছোট টাইপো সহ্য ("microsfot"-এও "Microsoft" পেতে পারে), যৌক্তিক “সেরা ফলাফল” অর্ডারিং (এক্সাক্ট আইডি বা ইমেইল উপরের দিকে), সামান্য রিসেন্সি বায়াস, এবং ডিফল্টভাবে প্রয়োগ করা ফিল্টার (ওপেন টিকিট, অ্যাক্টিভ কাস্টমার)।

কঠিন অংশ হলো, এক ইনপুট প্রায়ই একাধিক ইন্টেন্ট ঢেকে রাখে। একজন এজেন্ট টিকিট নম্বর পেস্ট করতে পারে, নামে এক ফ্র্যাগমেন্ট টাইপ করতে পারে, ইমেইল সার্চ করতে পারে, বা ফোন নাম্বার এন্টার করতে পারে। প্রতিটি ইন্টেন্ট ভিন্ন কৌশল, ভিন্ন ইনডেক্স এবং কখনও কখনও ভিন্ন র‍্যাঙ্কিং নিয়ম চায়।

তাই ইনডেক্স দিয়ে শুরু করবেন না। আগে আপনার ব্যবহারকারীরা কোন কয়েকটি সার্চ ইন্টেন্ট আসলে করে তা লিস্ট করুন, এবং আইডেন্টিটি ফিল্ড (IDs, emails) আলাদা করুন ফাজি ফিল্ড (names, subjects) ও লম্বা টেক্সট (notes) থেকে।

ডেটা এবং সার্চ আচরণগুলো নামকরণ করে শুরু করুন

কোন ইনডেক্স বাছবেন তার আগে লিখে নিন মানুষ ঠিক কী টাইপ করে। “PostgreSQL search everywhere” এক ফিচার শোনালেও, প্রকৃতপক্ষে এটি বেশ ভিন্ন সার্চের মিশ্রণ।

ইন্টারনাল টুলগুলো “হার্ড” আইডেন্টিফায়ার (order ID, ticket number, invoice code) ও “সফট” টেক্সট (customer name, email, notes, tags) মিশায়। এই গ্রুপগুলো PostgreSQL-এ ভিন্নভাবে আচরণ করে, তাই সবকিছুকে একইভাবে ট্রিট করা ধীর কুয়েরির দিকে চালিত করে।

পরবর্তী ধাপে আচরণগুলো আলাদা করুন:

  • এক্সাক্ট লুকআপ: কেউ TCK-104883 খুঁজলে একেবারেই নির্দিষ্ট এক রেজাল্ট আশা করে।
  • ফাজি লুকআপ: কেউ john smth লিখলে নামে (এবং হয়তো ইমেইলে) নরম মিল চায় এবং সংক্ষিপ্ত তালিকা স্ক্যান করবে।
  • ফিল্টার-চালিত সার্চ: কেউ “Status = Open” এবং “Assigned to = Me” বেছে নিলে এটা মূলত ফিল্টারিং; টেক্সট বক্স সেকেন্ডারি।

শুরুতেই ঠিক করুন রেজাল্টগুলোকে র‍্যাংক করতে হবে কি না। র‍্যাঙ্কিং নোট ও বড় বর্ণনার ক্ষেত্রে গুরুত্বপূর্ণ। IDs ও ইমেইলের জন্য র‍্যাঙ্কিং প্রায়ই এলোমেলো মনে হয় এবং খরচ বাড়ায়।

একটি ছোট চেকলিস্ট সাধারণত যথেষ্ট:

  • কোন ফিল্ডগুলো প্রতিদিন সার্চ হবে?
  • কোন ইনপুট এক্সাক্ট (IDs, codes), ফাজি (names), বা লম্বা টেক্সট (notes)?
  • কোন ফিল্টার প্রায় প্রতিটি সার্চে প্রয়োগ হয়?
  • “সেরা মিল” অর্ডারিং দরকার কি, নাকি কোনো ম্যাচই চলবে?
  • টেবিল কত দ্রুত বাড়বে: হাজার, লক্ষ, না মিলিয়ন?

এই সিদ্ধান্তগুলো আগে নিলে পরে ইনডেক্স বাছাই গেসওয়ার্কের মতো লাগবে না।

বেসলাইন: এক্সাক্ট ম্যাচ এবং কেন ILIKE প্রায়ই সমস্যা তৈরি করে

সহজ জিনিসগুলো আগে লক করে নিন। অনেক অভ্যন্তরীণ স্ক্রিনে একটি সাধারণ B-tree ইনডেক্সই এক্সাক্ট ম্যাচের জন্য মুহূর্তের ফল দেয়—ID, অর্ডার নম্বর, ইমেইল, এক্সটার্নাল রেফারেন্স।

যদি কেউ এক্সাক্ট মান পেস্ট করে, নিশ্চিত করুন আপনার কুয়েরি সত্যিই এক্সাক্ট: WHERE id = ... বা WHERE email = ... সাধারণ ইনডেক্সে অত্যন্ত দ্রুত হতে পারে। ইমেইলের উপরে unique ইনডেক্স প্রায়ই দ্বিগুণ উপকার দেয়: গতি ও ডেটা কোয়ালিটি।

সমস্যা শুরু হয় যখন “search everywhere” গোপনে ILIKE-এ পরিণত হয়। একটি কুয়েরি যেমন name ILIKE '%ann%'-এ লিডিং ওয়াইল্ডকার্ড আছে, তাই PostgreSQL সাধারণ B-tree ইনডেক্স ব্যবহার করতে পারে না। এটি অনেক সারি পরীক্ষা করে, এবং টেবিল বাড়লে এটি পূর্বানুমেয়ভাবে ধীর হয়।

প্রিফিক্স সার্চ কাজ করতে পারে, কিন্তু শুধুমাত্র যখন প্যাটার্ন শুরুতে অ্যাংকর্ড: name ILIKE 'ann%'। তবুও বিবরণ গুরুত্বপূর্ণ (collation, কেস হ্যান্ডলিং, এবং আপনি যে এক্সপ্রেশনটি কুয়েরি করছেন সেটার উপর ইনডেক্স আছে কি না)। যদি আপনার UI কেস-ইনসেনসিটিভ হতে চায়, একটি সাধারণ পদ্ধতি হলো lower(name) কুয়েরি করা এবং একই এক্সপ্রেশনে ইনডেক্স তৈরি করা।

এটাও সাহায্য করে ঠিক করা যে “snappy” মানে কী:

  • ওয়ার্ম ক্যাশে-তে ডাটাবেস কাজের জন্য ~200 ms বা তার কম
  • নেটওয়ার্ক ও রেন্ডারিং মিলিয়ে 1 সেকেন্ডের বেশি না
  • সাধারণ সার্চের জন্য দৃশ্যমান লোডিং স্টেট না থাকা

এই লক্ষ্যগুলো থাকলে সিদ্ধান্ত করা সহজ হয় যে এক্সাক্ট ও প্রিফিক্সে আটকে থাকা যায় কি না, বা full-text বা trigram দরকার কি না।

কখন full-text search উপযুক্ত

লোকেরা ন্যাচারাল ল্যাঙ্গুয়েজ টাইপ করে এবং সিস্টেমটিকে সঠিক আইটেম খুঁজে বের করার আশা করে—কেবল এক্সাক্ট ম্যাচ নয়—তখন full-text search ভালো ফিট। টিকিট মেসেজ, অভ্যন্তরীণ নোট, লম্বা বর্ণনা, knowledge base আর্টিকেল, কল লগ এসব উদাহরণ।

বড় জয় হলো র‍্যাংকিং। দীর্ঘ তালিকা ফেরত দেওয়ার পরিবর্তে যেখানে সেরা ফলটি ডুবে থাকে, full-text search রিলেভেন্স দিয়ে সাজাতে পারে। অভ্যন্তরীণ টুলে সেটা গুরুত্বপূর্ণ: কারো উত্তর দরকার সেকেন্ডের মধ্যে, 50 সারি স্ক্যান করে নয়।

উচ্চ স্তরে full-text search-এর তিনটি অংশ আছে:

  • একটি tsvector (সার্চযোগ্য টেক্সট, স্টোর করা বা জেনারেট করা)
  • একটি tsquery (ব্যবহারকারী যা টাইপ করেছে, কুয়েরিতে রূপান্তরিত)
  • একটি ভাষা কনফিগারেশন (কীভাবে শব্দগুলো নর্মালাইজ হয়)

ভাষা কনফিগারেশনেই আচরণ দৃশ্যমান হয়। PostgreSQL সাধারণ stop words (যেমন “the”, “and”) সরিয়ে দেয় এবং স্টেমিং প্রয়োগ করে, তাই “pay”, “paid”, এবং “payment” ম্যাচ করতে পারে। এটি নোট ও মেসেজে ভালো, কিন্তু ছোট সাধারণ শব্দ খুঁজলে কখনও মানুষ খালি ফল পেতে বিস্মিত হতে পারে।

সাইনোনিমও এক সিদ্ধান্তের বিষয়। আপনার কোম্পানি যখন একই জিনিসের জন্য ভিন্ন শব্দ ব্যবহার করে (উদাহরণ “refund” বনাম “chargeback”), সেক্ষেত্রে সাইনোনিম সাহায্য করে, কিন্তু সময়ের সঙ্গে একটু দেখভাল লাগে। সাইনোনিম লিস্ট সংক্ষিপ্ত এবং বাস্তব ব্যবহার ভিত্তিক রাখুন।

প্র্যাকটিক্যাল উদাহরণ: “can’t login after reset” সার্চ করলে এমন টিকিটগুলো উঠা উচিত যেখানে বার্তায় লেখা আছে “cannot log in after password reset” — শব্দবিন্যাস আলাদা হলেও প্রাসঙ্গিক মিল পাওয়া। এই ধরনের “প্রাসঙ্গিক খুঁজে পাওয়া” আচরণই full-text search-এর প্রধান সুবিধা এবং এটি ILIKE-কে সার্চ ইঞ্জিন বানানোর চেয়ে সাধারণত ভালো।

কখন ট্রাইগ্রাম ইনডেক্স জিতবে

Extend Search With AI
স্মার্টার ইন্টারনাল সার্চ বা টিকেট ট্রায়াজের জন্য AI ইন্টিগ্রেশন যোগ করুন।
AI দিয়ে বাড়ান

ট্রাইগ্রাম ইনডেক্স শক্তিশালী যখন ব্যবহারকারী ফ্র্যাগমেন্ট টাইপ করে, টাইপো করে, বা শুধু “কিছু মিল আছে” মনে রাখে। এগুলো ছোট টেক্সট ফিল্ডে উজ্জ্বল—যেখানে full-text খুব স্ট্রিক্ট: ব্যক্তির নাম, কোম্পানি নাম, টিকিট সাবজেক্ট, SKU, অর্ডার নম্বর, প্রোডাক্ট কোড।

একটি ট্রাইগ্রাম হল 3-কারেক্টার চাঙ্ক। PostgreSQL দুটি স্ট্রিং কতগুলো ট্রাইগ্রাম শেয়ার করে তার উপর তুলনা করে। তাই এটা মিলায় "Jon Smth" কে "John Smith"-এর সাথে, বা "ACM" কে "ACME"-এর সাথে, এবং যখন কুয়েরি শব্দের মাঝখানের অংশ পর্যন্ত হয় তখনও ফল দিতে পারে।

যদি কাজটি “ঠিক সেই সারি খুঁজে বের কর” হয়, কাগজপত্রের বিষয় নয়, ট্রাইগ্রাম প্রায়ই দ্রুত ও সহনশীল সমাধান।

কোথায় এটি full-text কে হারায়

Full-text লম্বা টেক্সট ও অর্থভিত্তিক র‍্যাংকিংয়ের জন্য চমৎকার, কিন্তু ছোট ফিল্ডে আংশিক স্ট্রিং বা ছোট টাইপো স্বাভাবিকভাবে হ্যান্ডেল করে না। ট্রাইগ্রাম সেই ধরনের ফাজিনেসের জন্য তৈরি।

লিখার খরচ যুক্তিসঙ্গত রাখুন

ট্রাইগ্রাম ইনডেক্স বড় এবং রাইটে ওভারহেড বাড়ায়, তাই বাছাই করে ব্যবহার করুন। এমন কলামগুলো ইনডেক্স করুন যেগুলো মানুষ বাস্তবে সার্চ বারে টাইপ করে:

  • Name, email, company, username
  • ছোট indentifiers (SKU, code, reference)
  • সংক্ষিপ্ত টাইটেল ফিল্ড (বড় নোট/কমেন্ট ফিল্ড নয়)

আপনি যদি নির্দিষ্ট কলামগুলো নাম করতে পারেন যেগুলো টিম সার্চ বারে টাইপ করে, ট্রাইগ্রাম ইনডেক্সিং সাধারণত ছোট ও দ্রুত রাখা যায়।

পার্শিয়াল ইনডেক্স — মানুষ যে ফিল্টারগুলো ব্যবহার করে সেগুলোই কভার করুন

Get Your Data Model Right
আইডেন্টিটি ফিল্ড ও ফাজি ফিল্ড দ্রুত আলাদা করে রাখুন যাতে ডেটা বাড়ার সঙ্গে অ্যাপ দ্রুত থাকে।
মডেল ডিফাইন করুন

একটি “search everywhere” বক্স সাধারণত গোপন ডিফল্ট নিয়ে আসে। মানুষ ওয়ার্কস্পেসের ভিতরে সার্চ করে, অ্যাক্টিভ আইটেম দেখায়, ডিলেটেড বাদ দেয়। যদি ওই ফিল্টারগুলো প্রায় প্রতিটি রিকোয়েস্টে থাকে, কেননা কমন কেসকে দ্রুত করতে সেই রোয়গুলোই ইনডেক্স করুন।

পার্শিয়াল ইনডেক্স হলো একটি নর্মাল ইনডেক্স যার সাথে WHERE ক্লজ থাকে। PostgreSQL শুধুমাত্র আপনার মনগঠিত সারিগুলোর এন্ট্রি রাখে, তাই ইনডেক্স ছোট থাকে। এর মানে কম পেজ পড়তে হয় এবং ক্যাশ হিট রেট ভালো থাকে।

কমন পার্শিয়াল-ইনডেক্স টার্গেটগুলো:অ্যাক্টিভ রোয় ( status = 'active' ), সফট ডিলিট (deleted_at IS NULL), টেন্যান্ট স্কোপিং, এবং সাম্প্রতিক উইন্ডো (উদাহরণ—শেষ ৯০ দিন)।

মূল বিষয় হলো UI-র সাথে মিল করা। যদি স্ক্রিন সবসময় ডিলেটেড সারি হাইড করে, আপনার কুয়েরি সবসময় deleted_at IS NULL থাকা উচিত, এবং আপনার পার্শিয়াল ইনডেক্সও একই কন্ডিশন ব্যবহার করা উচিত। ছোট মিল না থাকা, যেমন এক জায়গায় is_deleted = false আর অন্য জায়গায় deleted_at IS NULL ব্যবহারে প্ল্যানার ইনডেক্স ব্যবহার করা বন্ধ করতে পারে।

পার্শিয়াল ইনডেক্স full-text ও trigram ইনডেক্সের সাথে পাশাপাশি কাজ করে। উদাহরণস্বরূপ, নন-ডিলেটেড রোয়গুলোর জন্য কেবল টেক্সট সার্চ ইনডেক্স করা ইনডেক্স সাইজ নিয়ন্ত্রণে রাখে।

ট্রেড-অফ: পার্শিয়াল ইনডেক্স বিরল কুয়েরির জন্য কম সহায়ক। কেউ কখনও ডিলেটেড রেকর্ড বা সব ওয়ার্কস্পেস জুড়ে সার্চ করলে PostgreSQL ধীর প্ল্যান কাজে লাগাতে পারে। এই ক্ষেত্রে একটি আলাদা অ্যাডমিন-পাথ রাখুন, বা বিরল কুয়েরি সাধারণ হলে দ্বিতীয় ইনডেক্স যোগ করুন।

পদ্ধতিগুলো মিশিয়ে সার্চকে রহস্যে না পরিণত করা

অধিকাংশ টিম টেকনিক মিশিয়ে ব্যবহার করে কারণ এক সার্চ বক্সে বিভিন্ন ইন্টেন্ট থাকতে হয়। লক্ষ্য হলো অপারেশন অর্ডার স্পষ্ট রাখা যাতে রেজাল্ট প্রেডিক্টেবল লাগে।

একটি সরল প্রায়োরিটি অর্ডার সাহায্য করে—আপনি এটাকে আলাদা কুয়েরি হিসেবে বা একটি কুয়েরিতে CASE লজিক দিয়ে ইমপ্লিমেন্ট করতে পারেন।

একটি প্রেডিক্টেবল প্রায়োরিটি ল্যাডার

স্ট্রিক্ট থেকে ফাজি দিকে যান, যতক্ষণ প্রয়োজন:

  • প্রথমে এক্সাক্ট ম্যাচ (IDs, email, ticket number, SKU) B-tree ইনডেক্স দিয়ে
  • যেখানে যুক্তিসংগত প্রিফিক্স ম্যাচ
  • তারপর ট্রাইগ্রাম মিল টাইপো ও ফ্র্যাগমেন্টের জন্য
  • সর্বশেষে লম্বা নোট, ডেসক্রিপশন এবং ফ্রি-ফর্ম কনটেন্টের জন্য full-text

একই ল্যাডার মেনে চললে ব্যবহারকারীরা বুঝতে শিখবে বক্সের “অর্থ” কী। তারা ভাবা বন্ধ করবে যে সিস্টেম টুটেছে যখন “12345” একটি টিকিট তাত্ক্ষণিকভাবে পায় কিন্তু “refund policy” লম্বা টেক্সটে খোঁজ করে।

আগে ফিল্টার, তারপর ফাজি

ফাজি সার্চ তখনই ব্যয়বহুল হয় যখন এটি পুরো টেবিল বিবেচনা করে। ব্যবহারকারীরা যে ফিল্টারগুলো বাস্তবে ব্যবহার করে (status, assigned team, date range, account) সেগুলো দিয়ে ক্যান্ডিডেট সেট সংকুচিত করুন, তারপর বাকি অংশে ট্রাইগ্রাম বা full-text চালান। এমনকি দ্রুত ট্রাইগ্রাম ইনডেক্সও মিলিয়নগুলো রো কনসিডার করলে ধীর মনে হতে পারে।

এছাড়া একজন-প্যারাগ্রাফের নিয়ম লেখা উচিত যাতে নন-টেকনিকাল সহকর্মীরাও বুঝতে পারে, যেমন: “আমরা প্রথমে টিকিট নম্বর এক্সাক্ট ম্যাচ করি, তারপর কাস্টমার নাম টাইপো সহ্য করে মিলাই, তারপর নোট সার্চ করি।” এই শেয়ার্ড ডেফিনিশন পরে বিরোধ ঠেকায় কেন কোনো সারি দেখায় তার ওপর।

ধাপে ধাপে: পদ্ধতি বেছে নিয়ে নিরাপদে ইমপ্লিমেন্ট করুন

Deploy Where You Need
আপনার ইন্টারনাল টুল AppMaster Cloud-এ বা আপনার নিজস্ব AWS, Azure, Google Cloud-এ ডিপ্লয় করুন।
এখন ডিপ্লয় করুন

দ্রুত “search everywhere” বক্সটি ছোট ছোট সিদ্ধান্তের সেট। প্রথমে সেগুলো লিখে নিন, তারপর ডাটাবেস কাজ সহজ হয়।

  1. Define the inputs. এটা কি কেবল একটি বক্স, না বক্স + ফিল্টার (status, owner, date range)?
  2. ক্ষেত্র অনুযায়ী ম্যাচ টাইপ ঠিক করুন. IDs ও কোড এক্সাক্ট চান। নাম ও ইমেইল প্রায়ই প্রিফিক্স বা ফাজি প্রয়োজন। লম্বা নোট ও ডিসক্রিপশন ন্যাচারাল ল্যাঙ্গুয়েজ সার্চে ভালো।
  3. সঠিক ইনডেক্স যোগ করুন এবং নিশ্চিত করুন সেগুলো ব্যবহার হচ্ছে। ইনডেক্স তৈরি করুন, তারপর আপনার বাস্তব কুয়েরি নিয়ে EXPLAIN (ANALYZE, BUFFERS) চালান।
  4. ইচ্ছা অনুযায়ী র‌্যাঙ্কিং বা সর্টিং যোগ করুন। যদি ইউজার “invoice 1042” টাইপ করে, এক্সাক্ট ম্যাচ উপরে উঠা উচিত। যদি কেউ মিসস্পেলড নাম টাইপ করে, similarity র‌্যাঙ্কিং বিজয়ী হওয়া উচিত।
  5. বাস্তব কুয়েরি নিয়ে টেস্ট করুন: টাইপো, খুব ছোট টার্ম (যেমন “al”), লম্বা পেস্টেড টেক্সট, খালি ইনপুট, এবং “শুধু ফিল্টার” মোড।

নিরাপদভাবে শিপ করতে একবারে একটাই পরিবর্তন করুন এবং রোলব্যাক সহজ রাখুন। বড় টেবিলে নতুন ইনডেক্সের জন্য CREATE INDEX CONCURRENTLY পছন্দ করুন যাতে রাইট ব্লক না হয়। যদি পারেন, ফিচার ফ্ল্যাগের পিছনে শিপ করুন এবং ল্যাটেন্সি আগে ও পরে তুলনা করুন।

“PostgreSQL search everywhere” এর একটি বায়বীয় প্যাটার্ন: প্রথমে এক্সাক্ট ম্যাচ (দ্রুত ও নির্দিষ্ট), মানুষের ফিল্ডে ট্রাইগ্রাম ম্যাচ (টাইপো সহ্য করে), আর লম্বা টেক্সটের জন্য full-text search (র‍্যাংকিংয়ের সুবিধা)।

বাস্তব উদাহরণ: সাপোর্ট অ্যাডমিন প্যানেলে একটি সার্চ বক্স

একটি সাপোর্ট অ্যাডমিন প্যানেল কল্পনা করুন যেখানে টিম একটি সার্চ বক্স চান, কিন্তু তারা আশা করে এটি কাস্টমার, টিকিট, এমনকি নোটও খুঁজে পাবে। এটি “এক ইনপুট, বহু অর্থ” সমস্যার ক্লাসিক উদাহরণ।

প্রথম জয় হলো ইন্টেন্ট দৃশ্যমান করা ছাড়াই ঘা করা। যদি কুয়েরি একটি ইমেইল বা ফোন নম্বরের মত মনে হয়, এটি কাস্টমার লুকআপ হিসেবে ট্রিট করুন। যদি এটি টিকিট আইডির মতো লাগে (উদাহরণ, TKT-10482), সরাসরি টিকিটস্ পথে রাউট করুন। বাকি সবকিছু টিকিট সাবজেক্ট ও নোট জুড়ে টেক্সট সার্চে fallback।

কাস্টমার লুকআপের জন্য ট্রাইগ্রাম ইনডেক্স সাধারণত ভালো লাগে। নাম ও কোম্পানি স্ট্রিং গুলা ঝামেলাপূর্ণ এবং ব্যবহারকারী ফ্র্যাগমেন্ট টাইপ করে—ট্রাইগ্রাম “jon smi” বা “acm” মতো সার্চ দ্রুত এবং সহনশীল করে।

টিকিট নোটগুলোর জন্য full-text search ব্যবহার করুন। নোটগুলো প্রকৃত বাক্য এবং আপনি সাধারণত প্রাসঙ্গিক মিল চান, না “এই সাবস্ট্রিং আছে” টাইপের ফল। যখন একই কীওয়ার্ড বহু টিকিটে আসে, র‍্যাংকিং সাহায্য করে সেরা মিল উপরে আনতে।

ফিল্টারগুলো বেশী গুরুত্বপূর্ণ যেটা অধিকাংশ টিম অনুমান করে না। যদি এজেন্টরা “open tickets”-এ বাস করে, একটি partial index যোগ করুন যা কেবল open rows কভার করে। একইভাবে “active customers”-এর জন্য করুন। এটি ইনডেক্স ছোট রাখে এবং কমন পাথ দ্রুত করে।

খুব ছোট কুয়েরির নিয়ম থাকা উচিত, নতুবা ডাটাবেসে অনেক ব্যয়বহুল কাজ ট্রিগার হবে:

  • 1–2 ক্যারেক্টার: রিসেন্ট ওপেন টিকিট ও রিসেন্টলি আপডেট হওয়া কাস্টমার দেখান
  • 3+ ক্যারেক্টার: কাস্টমার ফিল্ডে ট্রাইগ্রাম চালান এবং টিকিট টেক্সটের জন্য full-text চালান
  • স্পষ্ট ইন্টেন্ট না থাকলে: একটি মিক্সড লিস্ট দেখান, প্রতিটি গ্রুপ কেপ করুন (উদাহরণ: 10 কাস্টমার ও 10 টিকিট)

সাধারণ ভুলগুলো যেগুলো সার্চ ধীর বা বিভ্রান্ত করে

Own Your Stack
সেলফ-হোস্টিং এবং গভীর কাস্টমাইজেশনের জন্য সোর্স এক্সপোর্ট করে স্ট্যাক আপনারা কন্ট্রোল করুন।
সোর্স এক্সপোর্ট করুন

অধিকাংশ “কেন সার্চ ধীর?” বাগ নিজে-ই সৃষ্টি করা হয়। লক্ষ্য ইনডেক্স সবকিছু নয়, বরং তারা যা ব্যবহার করে সেটাই ইনডেক্স করা।

একটি সাধারণ ফাঁদ হলো অনেক কলামে ইনডেক্স যোগ করা “হয়তো কাজে লাগবে” ভাবেই। পড়া ভাল হতে পারে, কিন্তু প্রতিটি ইনসার্ট এবং আপডেটে অতিরিক্ত কাজ যোগ হয়। অভ্যন্তরীণ টুল যেখানে রেকর্ড সারাদিন বদলে (টিকিট, অর্ডার, ইউজার), সেখানে রাইট স্পিড গুরুত্বপূর্ণ।

আরেকটি ভুল হলো full-text ব্যবহার করা যেখানে প্রকৃত প্রয়োজন নাম বা ইমেইলে টাইপো-সহনশীল লুকআপ। Full-text ডকুমেন্ট ও ডিসক্রিপশনের জন্য চমৎকার; এটা “Jon” বনাম “John” বা “gmail.con” বনাম “gmail.com” এর মতো টাইপো-টলার্যান্ট অটোকমপলিট নয়—সেটা সাধারণত ট্রাইগ্রামের কাজ।

ফিল্টারও নিঃশব্দে আপনার প্ল্যান ভেঙে দিতে পারে। যদি অনেক সার্চ একটি নির্দিষ্ট ফিল্টার দিয়ে ঘটে (যেমন status = 'open' বা org_id = 42), শ্রেষ্ঠ ইনডেক্সটি হতে পারে এমন একটি partial index যা ওই কন্ডিশন মেলে। ভুলে গেলে PostgreSQL অনেক বেশি সারি স্ক্যান করতে পারে।

কয়েকটি বারবার দেখা ভুল:

  • পরিমাপ না করে অনেক ইনডেক্স যোগ করা, ফলে রাইট কস্ট বাড়ে
  • full-text কে টাইপো-টলার্যান্ট অটোকমপলিট হিসেবে আশা করা
  • কমন ফিল্টার কীভাবে ইনডেক্স প্রভাবিত করে তা উপেক্ষা করা
  • ছোট, পরিষ্কার ডাটার উপর টেস্ট করা এবং বাস্তব টার্ম ফ্রিকোয়েন্সি না দেখা
  • এমন একটি কলামে সোর্ট করা যেটার সাথে কোন ইনডেক্স নেই, ফলে ধীর সোর্ট হওয়া

উদাহরণ: একটি সাপোর্ট স্ক্রিন টিকিট সার্চ করে সাবজেক্ট, কাস্টমার নাম, টিকিট নম্বর দিয়ে, তারপর সর্বশেষ activity দ্বারা সর্ট করে। যদি latest_activity_at ফিল্টারড সেটের জন্য ইনডেক্স না থাকে (উদাহরণ, ওপেন টিকিটের জন্য), সেই সোর্ট আপনার সার্চ ইনডেক্স থেকে পাওয়া গতি নষ্ট করে দিতে পারে।

শিপ করার আগে দ্রুত চেকলিস্ট

Turn Search Rules Into UI
ডেটা ডিজাইনারে টেবিল ডিজাইন করুন, তারপর এক্সাক্ট, ফাজি এবং টেক্সট সার্চ ফ্লো যুক্ত করুন।
বিল্ড শুরু করুন

“Search everywhere” ফিচার সম্পন্ন শ্বাশর আগে আচরণ সম্পর্কে স্পষ্ট হোন:

  • মানুষ কি রেকর্ড খুঁজছে একটি এক্সাক্ট আইডেন্টিফায়ার দিয়ে (টিকিট নম্বর, ইমেইল)?
  • তারা টাইপো-সহনশীল মিল প্রত্যাশা করে কি?
  • তারা লম্বা নোট ও ডিসক্রিপশনের র‍্যাংকড রেজাল্ট চায় কি?

মোড মিশালে সিদ্ধান্ত নিন কোনটি জিতবে যখন কনফ্লিক্ট হবে।

তারপর 2–3 টি ফিল্ড শনাক্ত করুন যেগুলো বেশি সার্চ চালায়। যদি 80% সার্চ হয় ইমেইল, নাম, টিকিট ID দ্বারা, সেগুলো আগে অপটিমাইজ করুন এবং বাকি সব সেকেন্ডারি ভাবুন।

একটি ছোট প্রি-শিপ চেকলিস্ট:

  • প্রতিটি ফিল্ডের মূল ম্যাচ মোড কনফার্ম করুন (exact lookup, fuzzy match, বা ranked text)
  • প্রতিদিন ব্যবহার হওয়া ফিল্টারগুলো লিস্ট করুন এবং নিশ্চিত করুন ইনডেক্সগুলো ঐ কম্বিনেশন মেলে
  • খুব ছোট ও খালি কুয়েরি কিভাবে হ্যান্ডেল করবেন ঠিক করুন (উদাহরণ: fuzzy search-এ 2–3 ক্যারেক্টার দরকার; খালি হলে “recent” দেখানো)
  • অর্ডারিং কিভাবে ব্যাখ্যা করবেন নির্ধারণ করুন: সবচেয়ে সাম্প্রতিক, সেরা টেক্সট ম্যাচ, বা একটি সরল কম্বাইনড রুল

অবশেষে, বাস্তব ডেটা আকার ও টাইমিং দিয়ে টেস্ট করুন, শুধু সঠিকতা নয়। 1,000 সারি দিয়ে দ্রুত মনে হওয়া কুয়েরি 1,000,000 সারি হলে ধীর হতে পারে।

পরবর্তী ধাপ: প্ল্যানকে দ্রুত অভ্যন্তরীণ সার্চ স্ক্রিনে রূপ দিন

একটি সার্চ বক্স তখনই দ্রুত থাকে যখন টিম একমত থাকে এটি কী করবে। সাধারণ ভাষায় নিয়মগুলো লিখে রাখুন: “ম্যাচ” কী (exact, prefix, typo-tolerant), কোন ফিল্ড সার্চ হবে, এবং ফিল্টার কিভাবে রেজাল্ট সেট বদলে দেবে।

একটি ছোট রিয়েল সার্চ টেস্ট সেট রাখুন এবং এটাকে রিগ্রেশন স্যুট হিসেবে ব্যবহার করুন। ১০–২০ কুয়েরি সাধারণত যথেষ্ট: কয়েকটি সাধারণ নাম, কয়েকটি আংশিক ইমেইল, একটি টাইপো, একটি লম্বা নোট স্নিপেট, এবং একটি “ফলাফল নেই” কেস। পরিবর্তনের আগে ও পরে এগুলো চালান যাতে পারফরম্যান্স কাজ ইর relevance নষ্ট না করে।

আপনি যদি AppMaster (appmaster.io) ব্যবহার করে অভ্যন্তরীণ টুল তৈরি করছেন, সার্চ নিয়মগুলো ডেটা মডেল ও ব্যবসায়িক লজিকের পাশে ডিফাইন করলে UI আচরণ ও ডাটাবেস পছন্দ বদলে যায় না।

প্রশ্নোত্তর

একটি ইন্টারনাল টুলে “search everywhere” সাধারণত কী বোঝায়?

এটি বোঝানো উচিত: “আমি যেই রেকর্ডটা ভাবছি, সেটা দ্রুত খুঁজে পাও।” ব্রাউজ করা নয়। আগে ব্যবহারকারীদের কয়েকটি আসল ইন্টেন্ট (ID lookup, নাম/ইমেইল টাইপ করে টাইপোগ্রাফি সহ খোঁজা, বড় নোট সার্চ) ও ডিফল্ট ফিল্টারগুলো লিখে নিন। এই সিদ্ধান্তগুলো উইচ কুয়েরি চালাতে হবে এবং কোন ইনডেক্স দরকার তা বলে দেবে।

কেন `ILIKE '%...%'` সার্চ ধীর করে?

ILIKE '%term%'-এ লিডিং ওয়াইল্ডকার্ড থাকে, তাই PostgreSQL সাধারণ B-tree ইনডেক্স ব্যবহার করতে পারে না এবং বহু সারি স্ক্যান করে। ছোট টেবিলে ঠিকঠাক দেখাতে পারে, কিন্তু ডাটা বাড়লে দ্রুত ধীর হয়ে যায়। সাবস্ট্রিং বা টাইপো-সহ ম্যাচিং দরকার হলে ILIKE-এর বদলে trigram বা full-text ব্যবস্থার পরিকল্পনা করুন।

IDs বা ইমেইলগুলোর মতো এক্সাক্ট লুকআপের দ্রুত পদ্ধতি কী?

এক্সাক্ট কম্প্যারিসনের মতো WHERE id = $1 বা WHERE email = $1 ব্যবহার করুন এবং এগুলোকে B-tree (অften ইমেইলের জন্য unique) ইনডেক্স দিয়ে ব্যাক করুন। এক্সাক্ট লুকআপ সবচেয়ে সস্তা সার্চ এবং ফলাফলও predictable করে। যদি ইউজার ফুল টিকিট নাম্বার বা ইমেইল পেস্ট করে, প্রথমেই এই পথটি রাউট করুন।

কিভাবে কেস-ইনসেনসিটিভ প্রিফিক্স সার্চ করবেন যাতে ইনডেক্স ভাঙে না?

প্যাটার্নটি শুরুতে অ্যাঙ্কর্ড করলে prefix সার্চ কাজ করে: উদাহরণ name ILIKE 'ann%'। কেস-ইনসেনসিটিভ রাখার জন্য অনেক টিম lower(name) কুয়েরি করে এবং একই এক্সপ্রেশনে ইনডেক্স তৈরি করে যাতে প্ল্যানার সেটি ব্যবহার করতে পারে। যদি প্যাটার্ন শুরুতে অ্যাঙ্কর না থাকে, prefix সার্চ কাজ করবে না।

একটি সার্চ বক্সে কখন ট্রাইগ্রাম ইনডেক্স ব্যবহার করা উচিত?

যখন ইউজার ফ্র্যাগমেন্ট টাইপ করে, ছোট টাইপো করে বা কেবলই ‘কিছু মিলে যায়’ মনে থাকে—বিশেষত ছোট ফিল্ডে (নাম, সাবজেক্ট, কোড)—তখন trigram ইনডেক্স খুব কার্যকর। এটি মিডল-অফ-ওয়ার্ড ম্যাচ এবং নিয়ার-ম্যাচ টাইপের জন্য ভাল। তবে ট্রাইগ্রাম ইনডেক্স বড় হয় এবং রাইট-ওভারহেড বাড়ায়, তাই কেবল সেই কলামগুলিতেই লাগান যেগুলো ব্যবহার করা হয়।

কখন PostgreSQL full-text সার্চ ভালো বিকল্প?

Full-text সার্চ ব্যবহার করুন যখন মানুষ বাক্য বা কীওয়ার্ড লিখে লম্বা কনটেন্ট (নোট, মেসেজ, ডিসক্রিপশন, knowledge-base) সার্চ করে এবং relevance র‍্যাংকিং প্রত্যাশা করে। এটি স্টেমিং ও স্টপ-ওয়ার্ড রিমুভাল করে, যা প্রোসে ভালো কিন্তু খুব ছোট সাধারণ শব্দ সার্চে সবসময় প্রত্যাশিত ফল নাও দিতে পারে।

কিভাবে partial indexes “search everywhere” স্ক্রিনগুলোতে সাহায্য করে?

যখন অধিকাংশ কুয়েরি একই ফিল্টার ব্যবহার করে (যেমন deleted_at IS NULL, status = 'open', বা tenant constraint), তখন partial index যোগ করুন। ইনডেক্সটি কেবল সাধারণ subset কভার করায় ছোট থাকে এবং রিয়েল-ওয়ার্কলোডে দ্রুত হয়। নিশ্চিত করুন আপনার কুয়েরি ঠিক একই কন্ডিশন ব্যবহার করে, নতুবা PostgreSQL ইনডেক্সটি ব্যবহার নাও করতে পারে।

আমি কিভাবে এক্সাক্ট, ট্রাইগ্রাম ও ফুল-টেক্সট মিলিয়ে ব্যবহার করে ব্যবহারকারীকে বিভ্রান্ত না করবো?

একটি কনসিস্টেন্ট priority ladder ব্যবহার করুন যাতে রেজাল্ট প্রেডিক্টেবল লাগে: প্রথমে এক্সাক্ট (ID/ইমেইল), তারপর প্রিফিক্স যেখানে প্রাসঙ্গিক, এরপর ট্রাইগ্রাম নাম/টাইটেল-এ ফাজি ম্যাচ, এবং লম্বা নোটের জন্য full-text। ডিফল্ট ফিল্টার আগে প্রয়োগ করুন যেন ফাজি সার্চকে পুরো টেবিল বিবেচনা করতে না হয়।

1–2 ক্যারেক্টারের সার্চ বা খালি ইনপুটের জন্য কী করা উচিত?

১–২ ক্যারেক্টারের জন্য ফাজি সার্চ চালানো ব্যয়বহুল ও শব্দ-উৎপাদক হতে পারে। সাধারণ নিয়ম রাখুন—উদাহরণ: 3+ ক্যারেক্টার হলে ফাজি চালান, আর ছোট ইনপুটে রিসেন্ট বা ফ্রিকোয়েন্ট রেকর্ড দেখান। খালি ইনপুটে “match everything” না চালিয়ে, কনসাইস রিক্সাল্ট দেখান।

কিভাবে পারফরম্যান্স যাচাই করে নিরাপদে সার্চ পরিবর্তন শিপ করবো?

ইনডেক্স তৈরি করে তারপর বাস্তব কুয়েরি নিয়ে EXPLAIN (ANALYZE, BUFFERS) চালান—ডেভ ডাটাসেটে নয়, রিয়ালিস্টিক ডাটা সাইজে। পরিবর্তনগুলো একটিমাত্র সময়ে রোল আউট করুন এবং বড় টেবিলে নতুন ইনডেক্স CREATE INDEX CONCURRENTLY দিয়ে বানান যাতে রাইট ব্লক না হয়। যদি AppMaster (appmaster.io) ব্যবহার করে বিল্ড করেন, সার্চ নিয়মগুলো ডেটা মডেলের কাছে ডিফাইন করুন যাতে UI আচরণ ও DB চয়েস আলাদা না হয়।

শুরু করা সহজ
কিছু আশ্চর্যজনকতৈরি করুন

বিনামূল্যের পরিকল্পনা সহ অ্যাপমাস্টারের সাথে পরীক্ষা করুন।
আপনি যখন প্রস্তুত হবেন তখন আপনি সঠিক সদস্যতা বেছে নিতে পারেন৷

এবার শুরু করা যাক