من البحث بـ LIKE إلى محرك بحث احترافي: رحلتي مع الفهرس المعكوس و BM25

يا أهلاً وسهلاً فيكم، معكم أخوكم أبو عمر.

قبل كم سنة، اتصل فيي صديق قديم، خلينا نسميه “أبو خليل”، كان عنده موقع ببيع فيه تحف ومنتجات يدوية تراثية. كان متحمّس جداً لمشروعه، لكن كان عنده مشكلة قاتلة: الزباين بدخلوا الموقع، ببحثوا عن منتج معين، وما بلاقوا إشي، فبطلعوا فوراً. “يا أبو عمر،” حكالي بصوت محبط، “عندي ألف قطعة أثرية على الموقع، والناس مش لاقية إشي! المبرمج اللي عندي عامل بحث عادي، والنتائج بطيئة ومخبّصة. شو الحل؟”

لما فحصت الموقع، اكتشفت الكارثة. كان البحث عبارة عن استعلام بسيط في قاعدة البيانات: SELECT * FROM products WHERE name LIKE '%keyword%'. هاي الشغلة ممكن تمشي مع 100 منتج، بس مع آلاف المنتجات، كانت الصفحة تحتاج 10 ثواني لتحميل النتائج، والترتيب كان عشوائي تماماً. بحثت عن “مصباح نحاسي قديم”، فطلعلي “خاتم فضة” في أول نتيجة لأنه في وصفه كلمة “قديم”.

هون كانت بداية رحلتنا لتغيير مفهوم البحث عند أبو خليل. قلتله: “اسمع يا خوي، بدنا نبني إشي مرتب، إشي زي جوجل بس لموقعك”. يومها، بنينا أول نسخة من محرك بحث حقيقي باستخدام المبادئ اللي رح أحكيلكم عنها اليوم. هاي القصة مش بس لأبو خليل، هاي لكل واحد فيكم عنده بيانات وبده يخلي الناس توصللها بسهولة وسرعة. يلا نشمّر عن إيدينا ونبدأ.

المشكلة: لماذا البحث التقليدي بـ `LIKE` لا يكفي؟

لنفترض أن لديك مدونة تحتوي على 500,000 مقالة، أو متجر إلكتروني به مليون منتج. عندما يأتي المستخدم ويكتب في مربع البحث، فإنه يتوقع شيئين رئيسيين: السرعة والصلة (Relevance). البحث باستخدام LIKE '%keyword%' في قاعدة بيانات SQL يفشل في تحقيق كليهما بشكل كارثي.

1. بطء قاتل في الأداء

عندما تستخدم LIKE مع علامات النسبة المئوية في البداية والنهاية (%...%)، فإنك تجبر قاعدة البيانات على إجراء فحص كامل للجدول (Full Table Scan). هذا يعني أنها ستقرأ كل سجل في الجدول، وتقارن النص الموجود في الحقل المطلوب مع كلمتك البحثية. مع مليون سجل، هذا يعني مليون عملية مقارنة نصية، وهي عملية بطيئة جداً وتستهلك موارد الخادم بشكل هائل.

2. نتائج غير مرتبة حسب الصلة

قاعدة البيانات لا تفهم “معنى” أو “أهمية” النتائج. هي فقط تبحث عن تطابق. فلو بحث المستخدم عن “هاتف ذكي”، قد تظهر له النتائج التالية بهذا الترتيب:

  1. “غطاء حماية لـهاتف ذكي
  2. “مقالة عن تاريخ تطور الهاتف الذكي
  3. هاتف ذكي جديد بمعالج سنابدراجون”

من الواضح أن النتيجة الثالثة هي الأهم والأكثر صلة، لكن قاعدة البيانات لا تملك آلية لترتيبها في المقدمة. هذا الإحباط يدفع المستخدم لمغادرة موقعك فوراً.

نصيحة من أبو عمر: إذا كان بحثك يستغرق أكثر من نصف ثانية، فأنت تخسر المستخدمين. في عالم اليوم، الصبر عملة نادرة.

الحل الحقيقي: الفهرس المعكوس وخوارزميات الترتيب

محركات البحث الاحترافية مثل Google, Elasticsearch, Solr, Meilisearch لا تعمل بهذه الطريقة البدائية. قلب هذه المحركات النابض يعتمد على مفهومين أساسيين: الفهرس المعكوس (Inverted Index) وخوارزميات الترتيب (Ranking Algorithms).

مفهوم الفهرس المعكوس (Inverted Index)

الفكرة عبقرية في بساطتها. بدلاً من البحث في الوثائق للعثور على كلمة، نقوم بإنشاء فهرس “معكوس” مسبقاً. هذا الفهرس هو ببساطة قاموس يربط كل كلمة فريدة بقائمة الوثائق (أو المقالات/المنتجات) التي تحتوي على هذه الكلمة.

تخيل عندنا 3 وثائق بسيطة:

  • وثيقة 1 (doc1): “الذكاء الاصطناعي هو المستقبل”
  • وثيقة 2 (doc2): “تعلم الذكاء الاصطناعي مع بايثون”
  • وثيقة 3 (doc3): “بايثون لغة المستقبل”

الفهرس المعكوس سيبدو كالتالي (مع معلومات إضافية مثل تكرار الكلمة):


{
  "الذكاء":   [(doc1, 1), (doc2, 1)],
  "الاصطناعي": [(doc1, 1), (doc2, 1)],
  "هو":       [(doc1, 1)],
  "المستقبل": [(doc1, 1), (doc3, 1)],
  "تعلم":     [(doc2, 1)],
  "مع":       [(doc2, 1)],
  "بايثون":   [(doc2, 1), (doc3, 1)],
  "لغة":      [(doc3, 1)]
}

عندما يبحث المستخدم عن “الذكاء بايثون”، كل ما نحتاجه هو:

  1. الذهاب إلى مفتاح “الذكاء” في الفهرس وأخذ قائمة الوثائق: [doc1, doc2].
  2. الذهاب إلى مفتاح “بايثون” وأخذ قائمة الوثائق: [doc2, doc3].
  3. إيجاد التقاطع بين القائمتين: doc2.

هذه العملية سريعة جداً، حتى مع ملايين الوثائق، لأننا نبحث في قاموس منظم بدلاً من فحص كل نص على حدة.

خوارزميات الترتيب: من TF-IDF إلى BM25

الآن بعد أن وجدنا الوثائق التي تحتوي على كلمات البحث، كيف نرتبها؟ هنا يأتي دور خوارزميات الترتيب.

  • TF-IDF (Term Frequency-Inverse Document Frequency): كانت الخوارزمية الكلاسيكية. فكرتها هي إعطاء وزن لكل كلمة في كل وثيقة. الكلمة تكون مهمة إذا:
    • تكررت كثيراً في الوثيقة نفسها (TF).
    • وكانت نادرة في مجموعة الوثائق كلها (IDF). كلمة مثل “هو” أو “في” ستحصل على وزن قليل لأنها شائعة جداً.
  • Okapi BM25 (Best Match 25): هي التطوير الحديث والأكثر فعالية المستخدم في معظم محركات البحث اليوم. هي تأخذ أفضل ما في TF-IDF وتضيف عليه تحسينين رئيسيين:
    1. تشبّع تكرار المصطلح (Term Frequency Saturation): في TF-IDF، إذا تكررت الكلمة 10 مرات، يكون وزنها ضعف وزن تكرارها 5 مرات. BM25 تقول “لحظة شوي!”. تكرار الكلمة 10 مرات يجعلها مهمة، لكن ليس بالضرورة ضعف أهمية تكرارها 5 مرات. بعد حد معين، لا تزيد الأهمية بنفس المعدل. هذا منطقي جداً.
    2. معايرة طول الوثيقة (Document Length Normalization): ظهور كلمة مرة واحدة في وثيقة من 10 كلمات هو أهم بكثير من ظهورها مرة واحدة في وثيقة من 1000 كلمة. BM25 تأخذ طول الوثيقة في الحسبان بشكل أكثر ذكاءً من TF-IDF.

هذه الآليات هي جوهر محركات البحث القوية مثل Lucene (الذي بُني عليه Elasticsearch و Solr).

مثال عملي: بناء محرك بحث لمدونة تقنية

لنجعل الأمر عملياً. لدينا مدونة تقنية ونريد بناء بحث “Google-like” لمقالاتها. سنمر بالخطوات من الصفر.

الخطوة 1: معالجة النصوص (Text Processing)

البيانات النصية “خام” وفوضوية. قبل بناء الفهرس، يجب تنظيفها وتوحيدها. هذه العملية تسمى Normalization.

  • Tokenization (الترميز): تقسيم النص إلى وحدات صغيرة تسمى “tokens” (عادةً هي الكلمات).

    "بناء محرك بحث احترافي" -> ["بناء", "محرك", "بحث", "احترافي"]
  • Normalization (التوحيد):
    • تحويل كل الحروف إلى حالة صغيرة (Lowercase) للغة الإنجليزية.
    • إزالة التشكيل (الفتحة، الضمة، إلخ) من النصوص العربية.
    • توحيد الحروف: (أ, إ, آ) -> (ا)، (ة) -> (ه)، (ي) -> (ى) أو العكس حسب المعيار.
    • إزالة علامات الترقيم والكلمات الشائعة جداً (Stop Words) مثل “في”, “من”, “على”, “the”, “a”, “is”.
  • Stemming/Lemmatization (التجذير/الإرجاع للأصل):
    • Stemming: عملية أبسط تقوم ببتر أواخر الكلمات للوصول إلى “جذع” مشترك. مثلاً، (“مكتبة”, “مكتبات”, “كاتب”) قد تصبح جميعها “مكتب”. قد لا يكون الجذر صحيحاً لغوياً لكنه يجمع الكلمات المرتبطة.
    • Lemmatization: عملية أكثر تعقيداً تستخدم قواعد لغوية لإرجاع الكلمة إلى أصلها المعجمي (lemma). مثلاً، “are”, “is” -> “be”. في العربية، “يكتبون” -> “كتب”.

الخطوة 2: بناء الفهرس المعكوس

بعد معالجة النصوص، سنقوم ببناء الفهرس. سنستخدم قاموس بايثون بسيط كمثال. لكل مقال (doc)، سنقوم بمعالجة نصه ونضيف كلماته (tokens) إلى الفهرس.


# هيكل بيانات مبسط للفهرس
# inverted_index = {
#   "token": {
#     "doc_frequency": 2, # عدد الوثائق التي تحتوي على الكلمة
#     "postings_list": [ (doc_id_1, term_frequency_1), (doc_id_2, term_frequency_2) ]
#   }
# }

# مثال
documents = {
    1: "الذكاء الاصطناعي هو المستقبل",
    2: "تعلم الذكاء الاصطناعي مع بايثون",
    3: "بايثون لغة المستقبل"
}

# بعد المعالجة والتنظيف (تخيل أننا قمنا بها)
processed_docs = {
    1: ["ذكاء", "اصطناعي", "مستقبل"],
    2: ["تعلم", "ذكاء", "اصطناعي", "بايثون"],
    3: ["بايثون", "لغه", "مستقبل"]
}

inverted_index = {}
doc_lengths = {}

for doc_id, tokens in processed_docs.items():
    doc_lengths[doc_id] = len(tokens)
    term_counts = {} # لحساب تكرار الكلمة في الوثيقة الحالية
    for token in tokens:
        term_counts[token] = term_counts.get(token, 0) + 1
    
    for token, tf in term_counts.items():
        if token not in inverted_index:
            inverted_index[token] = []
        inverted_index[token].append((doc_id, tf))

# الفهرس الناتج (بشكل مبسط)
# {
#   "ذكاء": [(1, 1), (2, 1)],
#   "اصطناعي": [(1, 1), (2, 1)],
#   ...
# }

الخطوة 3: البحث وحساب النقاط بـ BM25

عندما يبحث المستخدم، مثلاً عن “ذكاء المستقبل”، نقوم بالآتي:

  1. نعالج استعلام البحث: “ذكاء المستقبل” -> ["ذكاء", "مستقبل"].
  2. نجمع الوثائق المرشحة: نأخذ كل الوثائق التي تحتوي على “ذكاء” أو “مستقبل” من الفهرس. في مثالنا: doc1, doc2, doc3.
  3. نحسب نقاط BM25 لكل وثيقة مرشحة:

    معادلة BM25 قد تبدو معقدة، لكن فكرتها بسيطة. لكل كلمة في البحث، ولكل وثيقة، نحسب “نقطة” ثم نجمع النقاط.

    النقطة = جزء الـ IDF * جزء الـ TF المعدّل

    • جزء الـ IDF (Inverse Document Frequency): يقيس مدى ندرة الكلمة. كلما كانت أندر، زادت قيمته.
    • جزء الـ TF المعدّل: يقيس كم مرة ظهرت الكلمة في الوثيقة، مع الأخذ بعين الاعتبار طول الوثيقة الكلي وتشبع التكرار.

    هنا كود بايثون مبسط لتوضيح الفكرة (وليس للتطبيق الفعلي في بيئة الإنتاج):

    
    import math
    
    # ثوابت BM25، يمكن تعديلها
    k1 = 1.5
    b = 0.75
    
    N = len(documents) # العدد الكلي للوثائق
    avgdl = sum(doc_lengths.values()) / N # متوسط طول الوثيقة
    
    def calculate_bm25_score(query_tokens, doc_id):
        score = 0.0
        doc_len = doc_lengths[doc_id]
        
        for token in query_tokens:
            if token not in inverted_index:
                continue
                
            # 1. حساب IDF
            df = len(inverted_index[token]) # Document Frequency
            idf = math.log( (N - df + 0.5) / (df + 0.5) + 1.0 )
            
            # 2. حساب TF في الوثيقة الحالية
            tf = 0
            for doc, term_freq in inverted_index[token]:
                if doc == doc_id:
                    tf = term_freq
                    break
            
            # 3. حساب الجزء الخاص بالـ Term Frequency في معادلة BM25
            numerator = tf * (k1 + 1)
            denominator = tf + k1 * (1 - b + b * (doc_len / avgdl))
            
            score += idf * (numerator / denominator)
            
        return score
    
    # مثال: البحث عن "ذكاء مستقبل"
    query = ["ذكاء", "مستقبل"]
    scores = {}
    candidate_docs = {1, 2, 3} # كل الوثائق في مثالنا
    
    for doc_id in candidate_docs:
        scores[doc_id] = calculate_bm25_score(query, doc_id)
    
    # scores: {1: 1.38, 2: 0.69, 3: 0.69} (قيم تقريبية)
    # نرتب النتائج: doc1, ثم doc2 و doc3
    # النتيجة منطقية لأن doc1 يحتوي على الكلمتين معاً وهو قصير نسبياً.
    
  4. نرتب النتائج ونرجعها: نعرض النتائج للمستخدم مرتبة من الأعلى نقاطاً إلى الأقل.

الخطوة 4: تخزين الفهرس واستخدامه

بناء الفهرس من الصفر مفيد للتعلم، لكن في التطبيقات الحقيقية، لا تعيد اختراع العجلة! استخدم محركات بحث جاهزة وقوية تقوم بكل هذا العمل الشاق وأكثر.

  • Elasticsearch: هو المعيار الصناعي للمشاريع الكبيرة. قوي جداً، قابل للتوسع، وله مجتمع ضخم. يحتاج لبعض الخبرة لإعداده وإدارته.
  • Meilisearch: خيار رائع وحديث، يركز على السرعة وسهولة الاستخدام. “شغّل وامشي”. مثالي للمشاريع المتوسطة والصغيرة. إعداده بسيط جداً ويوفر نتائج فورية وتجربة مستخدم ممتازة.
  • بناء بسيط خاص بك: يمكنك تخزين الفهرس في ملف JSON أو في قاعدة بيانات NoSQL مثل Redis. هذا مناسب للمشاريع الصغيرة جداً أو لأغراض تعليمية، لكنه يفتقر للميزات المتقدمة وقابلية التوسع.

نصيحة من أبو عمر: ابدأ بـ Meilisearch إذا كان مشروعك جديداً أو متوسط الحجم. سهولته وسرعته ستدهشك. إذا كان مشروعك ضخماً جداً ويتطلب تحليلات معقدة، فـ Elasticsearch هو صديقك.

قياس الأداء: قبل وبعد

عندما طبقنا هذا الحل لموقع “أبو خليل”، كانت النتائج مذهلة. دعنا نقارن:

| المقياس | قبل (باستخدام `LIKE`) | بعد (باستخدام الفهرس المعكوس و BM25) |
|—|—|—|
| **زمن الاستجابة** | 5 – 10 ثوانٍ | 20 – 50 ميللي ثانية |
| **جودة النتائج** | غير مرتبة، نتائج غير ذات صلة في المقدمة | مرتبة بدقة حسب الصلة، المنتجات المطابقة تظهر أولاً |
| **تجربة المستخدم** | إحباط، معدل ارتداد (bounce rate) عالٍ | رضا، زيادة في مدة الجلسة ومعدل التحويل (conversion) |

أبو خليل اتصل بي بعد أسبوع وقال لي جملة لن أنساها: “يا أبو عمر، كأنك فتحت باب كان مسكّر! المبيعات زادت والناس مبسوطة”. هذه هي قوة البحث الحقيقي.

خلاصة الكلام والنصيحة الأخيرة 💡

بناء محرك بحث نصي فعال ليس سحراً، بل هو علم وهندسة. المبادئ الأساسية بسيطة وقوية:

  1. ودّع LIKE: إنه ليس أداة بحث، بل أداة لمطابقة الأنماط.
  2. احتضن الفهرس المعكوس: هو مفتاح السرعة الفائقة.
  3. استخدم BM25 للترتيب: هي الخوارزمية التي تضمن ظهور النتائج الأكثر صلة أولاً.
  4. لا تخترع العجلة في الإنتاج: تعلم المبادئ الأساسية، ولكن استخدم أدوات متخصصة مثل Elasticsearch أو Meilisearch لتوفير الوقت والجهد والحصول على أفضل النتائج.

فهمك لهذه المفاهيم سيجعلك مطوراً أفضل، قادراً على بناء أنظمة أذكى وأسرع. لن تكون مجرد مبرمج يكتب الكود، بل مهندساً يحل المشاكل الحقيقية التي تواجه المستخدمين. بالتوفيق يا جماعة! 👍

أبو عمر

سجل دخولك لعمل نقاش تفاعلي

كافة المحادثات خاصة ولا يتم عرضها على الموقع نهائياً

آراء من النقاشات

لا توجد آراء منشورة بعد. كن أول من يشارك رأيه!

آخر المدونات

أتمتة العمليات

قهوتك الصباحية مع ملخص الإنجازات: كيف تبني داشبورد يومي يصلك على الموبايل باستخدام n8n والذكاء الاصطناعي

كف عن تشتيت نفسك كل صباح بين Jira وGitHub والإيميلات. تعلم معي، أبو عمر، كيف تبني ورك فلو أتمتة يرسل لك ملخصاً ذكياً ومنسقاً بإنجازات...

12 فبراير، 2026 قراءة المزيد
البودكاست