ifone غير جملة تانية خالص
اليوزر كتب ifone وهو قاصد iphone: حرف ناقص. ده typo، و fuzzy search معمول عشانه. المسافة بين الكلمتين هي Levenshtein distance: كام إضافة أو حذف أو تبديل يخلّصوا الكلمة للتانية. هنا المسافة ١.
جملة زي “if no way” بدل iPhone مش typo في كلمة. دي كلمات تانية. الـ fuzzy مش هيحوّلها، ولو زوّدت المسافة المسموحة عشان تلحقها هترجع نتائج ملهاش علاقة. المرادفات والبحث الصوتي أدوات تانية، وسهلة تبوّظ الدقة.
الكويري وفيها سماحية الغلط
export function textQuery(q) { return { bool: { should: [ { term: { sku: { value: q.toUpperCase(), boost: 20 } } }, { multi_match: { query: q, fields: ["name^3", "description"], fuzziness: "AUTO", prefix_length: 1, }, }, ], minimum_should_match: 1, }, };}الـ SKU بره الـ fuzzy. لو اليوزر لصق كود، عايز تطابق كامل، ومش عايز كود قريب يطلّع منتج تاني. وحّد الـ SKU لـ uppercase وقت ما بتحفظه ووقت ما بتبحث، عشان apl-... و APL-... يبقوا نفس الـ keyword.
الـ fuzzy أغلى من التطابق العادي لأنه بيوسّع الكلمة لكذا احتمال. حطه على بحث الصفحة، مش على كل حرف في الاقتراحات. الاقتراحات فصل لوحدها.
Query context غير filter context
بعد ما لقينا نتائج قريبة، اليوزر يقول: Lenovo، رام ١٦، سعر من ١٠ لـ ٣٠ ألف، متوفر، تقييم ٤ وفوق.
سؤال: مين أقرب للنِيّة؟ بيحسب _score. مكانه must و should.
سؤال: الشرط متحقق؟ Lenovo يا آه يا لأ. مفيش score، والنتيجة بتتـcache. مكانه filter.
السعر والمخزون والتقييم والبراند فلاتر. متدخلهمش في حساب الصلة، عشان منتج أغلى مياخدش ترتيب أعلى بالصدفة، وعشان نفس الفلتر يتتعاد استخدامه من غير ما يتحسب من أول وجديد مع كل جملة بحث.
export function toNum(value) { if (value == null || value === "") return null; const n = Number(value); return Number.isFinite(n) ? n : null;}export function productFilters({ brand, minPrice, maxPrice, minRating, inStock, ramGb }) { const filters = []; if (brand) filters.push({ term: { brand } }); if (ramGb != null) filters.push({ term: { ram_gb: ramGb } }); if (minRating != null) filters.push({ range: { rating: { gte: minRating } } }); if (inStock) filters.push({ range: { stock: { gt: 0 } } }); if (minPrice != null || maxPrice != null) { const price = {}; if (minPrice != null) price.gte = minPrice; if (maxPrice != null) price.lte = maxPrice; filters.push({ range: { price } }); } return filters;}import { textQuery } from "./search-text.js";import { productFilters } from "./filters.js";export function searchBody(input) { const q = (input.q ?? "").trim(); return { query: { bool: { must: q ? [textQuery(q)] : [{ match_all: {} }], filter: productFilters(input), }, }, };}من غير نص بحث، match_all مع الفلاتر دي صفحة قسم: “لابتوبات Lenovo المتوفرة”. لسه مش facets. الأعداد اللي جنب البراند محتاجة aggregations، وفيها فخ هنشوفه في الفصل الجاي.
🧪 Lab
ابعت نفس الجملة مرتين: ifone من غير fuzziness، وبعدين بيها. الأولى غالبًا فاضية، والتانية ترجع iPhone. بعدين ضيف filter براند تاني. ترتيب الـ _score بين نتايج نفس البراند مبيتغيرش بسبب الفلتر، والبراند الغلط يختفي كله.