ابنِ محرك بحث وتوصية
التعليمات
في هذا المختبر، ستبني المكونات الأساسية لنظام بحث وتوصية منتجات من الصفر بلغة TypeScript. هذا هو النظام بالضبط الذي ستصممه في سؤال مقابلة "صمم بحث المنتجات للتجارة الإلكترونية" -- لكن هنا ستُنفّذه فعليًا.
نظرة عامة على البنية
المستندات (كتالوج المنتجات)
↓
المقسّم (تصغير الأحرف، كلمات التوقف، التجذير)
↓
الفهرس المقلوب (مصطلح → قوائم نشر مع TF)
↓
مسجّل BM25 (ترتيب حسب الصلة)
↓
نتائج البحث
سجل تفاعلات المستخدم
↓
┌────────────────────┬────────────────────┐
│ التصفية │ التصفية المبنية │
│ التعاونية │ على المحتوى │
│ (تشابه الجيب تمام │ (متجهات الخصائص) │
│ على تقييمات │ │
│ المستخدمين) │ │
└────────┬───────────┴─────────┬──────────┘
│ │
▼ ▼
المُوصي الهجين (مزج + بداية باردة)
↓
التوصيات
دليل خطوة بخطوة
ملف 1: المقسّم (src/search/tokenizer.ts)
ابنِ مقسّم نصوص يُحضّر النص الخام للفهرسة. يجب أن يقوم المقسّم بـ:
- تحويل النص إلى أحرف صغيرة
- التقسيم على الأحرف غير الأبجدية الرقمية (المسافات، علامات الترقيم)
- إزالة كلمات التوقف الإنجليزية (a, an, the, is, are, was, were, in, on, at, to, for, of, and, or, but, not, with, this, that, it, be, have, do, will, can، إلخ)
- تطبيق مجذّر بسيط يزيل اللواحق الإنجليزية الشائعة:
- "ing" -> إزالة (مثلًا "running" -> "runn")
- "ed" -> إزالة (مثلًا "played" -> "play")
- "s" في النهاية -> إزالة (مثلًا "shoes" -> "shoe")
- "ly" -> إزالة (مثلًا "quickly" -> "quick")
- أزل اللاحقة فقط إذا كان الجذر المتبقي 3 أحرف على الأقل
صدّر دالة tokenize(text: string): string[].
ملف 2: الفهرس المقلوب (src/search/inverted-index.ts)
ابنِ فهرسًا مقلوبًا يربط المصطلحات بقوائم النشر. يجب أن يقوم الفهرس بـ:
- قبول المستندات عبر
addDocument(docId: string, text: string) - تقسيم النص باستخدام المقسّم الخاص بك
- بناء قوائم نشر حيث يحتوي كل إدخال على
{ docId, termFrequency } - تتبع أطوال المستندات (عدد الرموز لكل مستند) لحساب BM25
- تتبع العدد الإجمالي للمستندات ومتوسط طول المستند
- توفير
getPostings(term: string): PostingEntry[]لاسترجاع قائمة النشر لمصطلح - توفير
getDocLength(docId: string): numberوgetAvgDocLength(): number
ملف 3: مسجّل BM25 (src/search/bm25-scorer.ts)
نفّذ خوارزمية ترتيب BM25. يجب أن يقوم المسجّل بـ:
- قبول الفهرس المقلوب كتبعية
- استخدام المعاملات القياسية: k1 = 1.2, b = 0.75
- تنفيذ
score(query: string, docId: string): numberلحساب درجة BM25 - تنفيذ
search(query: string, topK?: number): SearchResult[]لإرجاع أعلى K مستند مرتبة بدرجة BM25 - استخدام معادلة BM25 الصحيحة:
- IDF(t) = log((N - df + 0.5) / (df + 0.5) + 1)
- مساهمة الدرجة لكل مصطلح = IDF * (tf * (k1 + 1)) / (tf + k1 * (1 - b + b * docLen / avgDocLen))
ملف 4: الإكمال التلقائي (src/search/autocomplete.ts)
ابنِ خدمة إكمال تلقائي مبنية على البادئة. يجب أن تقوم الخدمة بـ:
- الاحتفاظ بقائمة مصطلحات مع درجات الشعبية
- قبول إدخالات جديدة عبر
addEntry(term: string, score: number) - تنفيذ
suggest(prefix: string, limit?: number): AutocompleteResult[]التي:- تجد جميع المصطلحات التي تبدأ بالبادئة (بأحرف صغيرة)
- ترتب حسب درجة الشعبية تنازليًا
- تعيد
limitنتيجة على الأكثر (افتراضي 10)
- دعم تحديث الدرجات عبر
updateScore(term: string, delta: number)(مثلًا زيادة 1 عند كل بحث)
ملف 5: التصفية التعاونية (src/recommendation/collaborative-filter.ts)
نفّذ التصفية التعاونية المبنية على المستخدم. يجب أن يقوم المرشّح بـ:
- تخزين تقييمات المستخدم-العنصر عبر
addRating(userId: string, itemId: string, rating: number) - حساب تشابه الجيب تمام بين مستخدمَين بناءً على العناصر المقيّمة مشتركًا
- تنفيذ
findSimilarUsers(userId: string, topK?: number): SimilarUser[] - تنفيذ
recommend(userId: string, topN?: number): Recommendation[]التي:- تجد أعلى K مستخدمين مشابهين
- تجمع العناصر التي قيّمها هؤلاء المستخدمون بدرجة عالية ولم يقيّمها المستخدم المستهدف
- تتنبأ بدرجة باستخدام المتوسط المرجح لتقييمات المستخدمين المشابهين
- تعيد أعلى N توصية مرتبة حسب الدرجة المتوقعة
ملف 6: التصفية بالمحتوى (src/recommendation/content-filter.ts)
نفّذ التصفية المبنية على المحتوى مع متجهات خصائص العناصر. يجب أن يقوم المرشّح بـ:
- تسجيل العناصر مع متجهات الخصائص عبر
addItem(itemId: string, features: number[]) - بناء ملف المستخدم بحساب متوسط متجهات خصائص العناصر التي أعجبته
- تنفيذ
buildUserProfile(likedItemIds: string[]): number[] - تنفيذ
recommend(likedItemIds: string[], topN?: number): Recommendation[]التي:- تبني متجه ملف المستخدم
- تحسب تشابه الجيب تمام بين ملف المستخدم وكل عنصر ليس في likedItemIds
- تعيد أعلى N عنصر مرتبة حسب التشابه
ملف 7: المُوصي الهجين (src/recommendation/hybrid-recommender.ts)
ابنِ مُوصيًا هجينًا يمزج بين المقاربتين التعاونية والمبنية على المحتوى. يجب أن يقوم المُوصي بـ:
- قبول كل من المرشّح التعاوني والمرشّح بالمحتوى كتبعيات
- تنفيذ
recommend(userId: string, likedItemIds: string[], topN?: number): Recommendation[] - معالجة البداية الباردة: إذا كان المستخدم لديه أقل من عتبة قابلة للتكوين (افتراضي 5) من التقييمات، إعطاء وزن أكبر لنتائج المحتوى (مثلًا 70% محتوى، 30% تعاوني)
- للمستخدمين النشطين: إعطاء وزن أكبر لنتائج التعاون (مثلًا 70% تعاوني، 30% محتوى)
- إزالة التكرار (نفس العنصر من كلا المصدرين): الاحتفاظ بالدرجة الأعلى
- إعادة النتائج النهائية مرتبة حسب الدرجة الممزوجة
ملف 8: نقطة الدخول الرئيسية (src/index.ts)
اربط كل شيء معًا في عرض توضيحي:
- إنشاء مستندات منتجات نموذجية (5 على الأقل)
- فهرستها باستخدام الفهرس المقلوب
- البحث باستخدام مسجّل BM25 وطباعة النتائج
- إضافة إدخالات الإكمال التلقائي وعرض الاقتراحات
- إعداد تقييمات المستخدمين وعرض توصيات التصفية التعاونية
- إعداد خصائص العناصر وعرض توصيات التصفية بالمحتوى
- عرض المُوصي الهجين مع مستخدم بداية باردة ومستخدم نشط
تلميحات
- للمجذّر، فحص بسيط للواحق كافٍ -- لست بحاجة لتنفيذ خوارزمية Porter Stemmer الكاملة
- لتشابه الجيب تمام، عالج حالة المتجه الصفري (أعد 0 عندما يكون طول أي من المتجهين صفرًا)
- في التصفية التعاونية، احسب التشابه فقط على العناصر التي قيّمها كلا المستخدمين (العناصر المقيّمة مشتركًا)
- للمُوصي الهجين، استخدم Map لإزالة التكرار حسب itemId والاحتفاظ بالدرجة الأعلى
- معادلة IDF في BM25 المستخدمة هنا تتضمن "+1" داخل log لتجنب IDF سالب للمصطلحات التي تظهر في أكثر من نصف المستندات