البحث النصي كان كابوسًا: كيف أنقذنا مشروعنا من جحيم استعلامات LIKE ‘%…%’؟

مقدمة: ليلة لا تُنسى مع استعلام “LIKE” اللعين

يا جماعة الخير، السلام عليكم ورحمة الله. اسمي أبو عمر، مبرمج فلسطيني قضيت سنين عمري بين الأكواد والخوارزميات. اليوم بدي أحكيلكم قصة صارت معي ومع فريقي، قصة فيها معاناة وسهر، بس نهايتها كانت درس كبير وفائدة عظيمة، إشي زي ما بحكوا “رُب ضارة نافعة”.

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

بالأول، قلنا يمكن ضغط على السيرفر، أو مشكلة مؤقتة بالشبكة. بس الشكاوي زادت، وصارت المشكلة واضحة زي عين الشمس: البحث هو الكارثة. دخلت على لوحة مراقبة أداء قاعدة البيانات (كانت PostgreSQL وقتها)، وشفت المنظر اللي كل مبرمج بخاف منه: استعلامات بتاخد 15 و 20 ثانية لتتنفذ، والمعالج تبع قاعدة البيانات واصل 100% ومش ملحّق. رفعت راسي عن الشاشة وسألت الشب اللي معي: “شو هو الاستعلام اللي عامل كل هالمصايب؟”.

الجواب كان متوقع ومؤلم بنفس الوقت: SELECT * FROM articles WHERE title LIKE '%search_term%' OR content LIKE '%search_term%';.

نعم، يا أصدقائي. استعلام LIKE مع علامة النسبة المئوية % في البداية. هذا الاستعلام البريء ظاهرياً كان هو المسؤول عن شلّ حركة التطبيق بالكامل. في هذيك الليلة، قعدنا أنا والفريق للصبح، بنحاول نلاقي حل سريع، وعرفت وقتها إنه الحلول السريعة ما بتنفع مع المشاكل العميقة. كان لازم نغير طريقة تفكيرنا بالبحث النصي من جذورها. ومن هنا بدأت رحلتنا لإنقاذ محرك البحث تبعنا.

لماذا يعتبر `LIKE ‘%…%’` كابوسًا حقيقيًا؟

عشان نفهم حجم المشكلة، لازم نفهم كيف بتشتغل قواعد البيانات العلائقية (زي MySQL, PostgreSQL, SQL Server). تخيل معي إنه عندك كتاب ضخم (قاعدة البيانات)، وفي آخر الكتاب فيه فهرس (Index) مرتب أبجديًا بأسماء الفصول والمواضيع المهمة. لو بدك تبحث عن كلمة “الذكاء الاصطناعي”، بتروح على حرف الألف في الفهرس، بتلاقيها بسرعة، وبتعرف بأي صفحات موجودة.

هذا الفهرس هو اللي بخلي استعلامات البحث سريعة. لما تبحث عن قيمة معينة في عمود عليه فهرس (Index)، قاعدة البيانات بتستخدمه عشان تلاقي البيانات بسرعة خيالية.

الكارثة: المسح الكامل للجدول (Full Table Scan)

طيب، شو بصير لما تستخدم LIKE '%search_term%'؟

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

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

نصيحة من أبو عمر: قاعدة بسيطة لازم تحفظها: أي استعلام يمنع قاعدة البيانات من استخدام الفهرس هو عدو للأداء. استعلام LIKE مع بادئة % هو العدو الأول في هذه القائمة.

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

في البداية، حاولنا نلاقي حلول “ترقيعية”. فكرنا في حلول مثل:

  • Full-Text Search المدمج: قواعد بيانات مثل PostgreSQL و MySQL بتقدم ميزات بحث نصي كامل (FTS). هي بالتأكيد أفضل من LIKE، لأنها بتبني فهارس خاصة للكلمات (بتشبه فكرة الفهرس المقلوب اللي رح نحكي عنها بعد شوي). جربناها، والأداء تحسن بشكل ملحوظ.
  • مشاكل الـ FTS المدمج: لكن واجهتنا مشاكل ثانية. التعامل مع اللغة العربية وتصريفاتها (stemming) ما كان مثالي. كلمة “مكتبة” و “مكتبات” و “كتبي” ما كانت تُعامل بنفس الطريقة دائمًا. بالإضافة إلى أن خيارات تخصيص “مدى أهمية” النتائج (Relevance Scoring) كانت محدودة، وقابلية التوسع (Scalability) على عدة سيرفرات كانت معقدة.

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

البطل المنقذ: محركات البحث المتخصصة (Elasticsearch)

بعد بحث ونقاش، قررنا نتبنى الحل الصحيح: استخدام محرك بحث متخصص ومستقل. وقع اختيارنا على Elasticsearch، وهو ليس مجرد اختيار، بل هو يعتبر المعيار الصناعي (Industry Standard) في هذا المجال اليوم.

ما هو Elasticsearch وكيف يعمل بسحره؟

ببساطة، Elasticsearch هو سيرفر بحث وتحليل بيانات مفتوح المصدر، مبني على مكتبة بحث قوية جدًا اسمها Apache Lucene. فكر فيه كقاعدة بيانات NoSQL موجهة للمستندات (Document-oriented)، لكن قوتها الخارقة تكمن في البحث.

السر ورا سرعته الخارقة هو مفهوم اسمه “الفهرس المقلوب” (Inverted Index).

نرجع لمثال الكتاب. الفهرس العادي بقولك: “صفحة 50 فيها الكلمات التالية…”. أما الفهرس المقلوب فبعمل العكس، بقولك: “كلمة ‘البرمجة’ موجودة في الصفحات 15, 40, 92” و “كلمة ‘البيانات’ موجودة في الصفحات 40, 101, 230”.

لما تبحث عن “البرمجة” و “البيانات”، Elasticsearch بروح على الفهرس المقلوب، بلاقي قائمة الصفحات لكل كلمة، بعمل تقاطع بينهم، وبقولك فورًا: “الكلمتين موجودين مع بعض في صفحة 40”. كل هذا بتم في أجزاء من الثانية، حتى لو عندك مليارات المستندات.

التطبيق العملي: من الفكرة إلى الكود

الكلام النظري جميل، بس خلينا نشوف كيف طبقنا هالحل عمليًا. العملية بتتقسم لـ 3 خطوات رئيسية.

الخطوة الأولى: تجهيز وفهرسة البيانات (Indexing)

أول خطوة هي أخذ البيانات من قاعدة بياناتنا الأساسية (PostgreSQL) ووضعها في Elasticsearch. هذه العملية تسمى “الفهرسة”.

كل سجل في جدول `articles` صار عبارة عن “مستند” (Document) بصيغة JSON في Elasticsearch. مثلاً، مقال بهذا الشكل في قاعدة البيانات:

-- SQL Table: articles
id: 123
title: "مقدمة في الذكاء الاصطناعي"
content: "الذكاء الاصطناعي هو فرع من فروع علوم الحاسوب..."
author: "أبو عمر"
created_at: "2023-10-26"

يتحول إلى مستند JSON بهذا الشكل في Elasticsearch:

// Elasticsearch Document
{
  "id": 123,
  "title": "مقدمة في الذكاء الاصطناعي",
  "content": "الذكاء الاصطناعي هو فرع من فروع علوم الحاسوب...",
  "author": "أبو عمر",
  "created_at": "2023-10-26"
}

كتبنا سكربت بسيط (باستخدام لغة Python ومكتبة `elasticsearch-py`) يقرأ كل المقالات من قاعدة البيانات، ويقوم بإرسالها إلى Elasticsearch لفهرستها. هذه العملية تتم مرة واحدة في البداية، ثم نحتاج لآلية مزامنة للتحديثات الجديدة (سنتحدث عنها لاحقًا).

الخطوة الثانية: استبدال كود البحث القديم

الآن الجزء الممتع. تخلصنا من استعلام `LIKE` البطيء واستبدلناه باستدعاء لواجهة برمجة التطبيقات (API) الخاصة بـ Elasticsearch.

الكود القديم (مثال بلغة Python/Flask):

# قبل: استخدام SQL LIKE
@app.route('/search')
def search():
    query = request.args.get('q')
    # استعلام بطيء ومدمر!
    sql_query = f"SELECT * FROM articles WHERE title LIKE '%%{query}%%' OR content LIKE '%%{query}%%'"
    # ... تنفيذ الاستعلام وإرجاع النتائج

الكود الجديد (باستخدام مكتبة Elasticsearch):

# بعد: استخدام Elasticsearch
from elasticsearch import Elasticsearch
es = Elasticsearch([{'host': 'localhost', 'port': 9200}])

@app.route('/search')
def search():
    query = request.args.get('q')
    
    # استعلام Elasticsearch سريع وذكي
    es_query = {
        "query": {
            "multi_match": {
                "query": query,
                "fields": ["title", "content"], # ابحث في العنوان والمحتوى
                "fuzziness": "AUTO" # للسماح بالأخطاء الإملائية البسيطة
            }
        }
    }
    
    results = es.search(index="articles_index", body=es_query)
    # ... معالجة النتائج وإرجاعها

لاحظ الفرق! لم نعد نرهق قاعدة البيانات الأساسية بعمليات البحث. كل ضغط البحث انتقل إلى Elasticsearch المصمم خصيصًا لهذه المهمة. النتيجة؟ سرعة استجابة بالمللي ثانية بدلًا من عشرات الثواني.

نصائح من خبرة أبو عمر 🤓

الانتقال إلى Elasticsearch كان نقلة نوعية، لكن الطريق كان فيه بعض المطبات اللي تعلمت منها دروس غالية. اسمحولي أشارككم إياها:

  • المزامنة هي مفتاح النجاح (Synchronization is Key): بياناتك موجودة في مكانين الآن (قاعدة البيانات الأساسية و Elasticsearch). كيف تحافظ على تطابقها؟ هناك عدة طرق:
    • الطريقة البسيطة (Cron Job): تشغيل سكربت بشكل دوري (كل 5 دقائق مثلاً) يبحث عن أي سجلات تم تعديلها أو إضافتها في قاعدة البيانات الأساسية ويقوم بتحديثها في Elasticsearch.
    • الطريقة المتقدمة (Message Queue): عند أي عملية إنشاء/تعديل/حذف في تطبيقك، بدلًا من الكتابة في قاعدة البيانات مباشرة، أرسل “رسالة” إلى نظام طوابير مثل RabbitMQ أو Kafka. يكون لديك “مستهلك” (Consumer) يقرأ هذه الرسالة، يحدث قاعدة البيانات، ثم يحدث Elasticsearch. هذه الطريقة تضمن التزامن شبه الفوري.
  • اهتم بالمحلِّلات (Analyzers)، خاصة للعربية: Elasticsearch يقوم بتحليل النص قبل فهرسته (تقطيعه لكلمات، حذف الكلمات الشائعة، إرجاع الكلمات لجذرها). المحلل الافتراضي لا يفهم اللغة العربية جيدًا. يجب أن تستخدم محللًا مخصصًا للغة العربية (مثل `arabic` analyzer المدمج) الذي يفهم تصريفات الكلمات وجذورها. هذا هو الفرق بين بحث “سطحي” وبحث “ذكي” يفهم قصد المستخدم.
  • Elasticsearch ليس بديلًا لقاعدة بياناتك الأساسية: هذه نقطة جوهرية. قاعدة بياناتك العلائقية (SQL) تبقى هي “مصدر الحقيقة” (Source of Truth). Elasticsearch هو فهرس ثانوي متخصص للبحث. لا تستخدمه كمخزن بياناتك الأساسي والوحيد.
  • ابدأ ببساطة: لا تحتاج إلى بناء عنقود (Cluster) ضخم من سيرفرات Elasticsearch من اليوم الأول. ابدأ بعقدة (Node) واحدة على نفس السيرفر أو سيرفر صغير، وعندما ينمو تطبيقك وتزداد بياناتك، يمكنك التوسع بسهولة لأن Elasticsearch مصمم للتوسع الأفقي.

الخلاصة: لكل مهمة أداتها المناسبة 🚀

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

إذا كنت تعاني من بطء البحث في تطبيقك، وتعتمد على LIKE '%...%'، فاعلم أنك لست وحدك، والحل موجود وفعّال. الاستثمار في تعلم وتطبيق محرك بحث متخصص مثل Elasticsearch ليس رفاهية، بل هو ضرورة لتحسين أداء تطبيقك وتقديم تجربة مستخدم رائعة.

لا تخف من تعلم تقنية جديدة قد تبدو معقدة في البداية. الفائدة التي ستحصل عليها على المدى الطويل تستحق كل دقيقة تقضيها في التعلم والتجربة. بالتوفيق يا جماعة الخير!

أبو عمر

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

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

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

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

آخر المدونات

تجربة المستخدم والابداع البصري

كان كل فريق يصمم على هواه: كيف أنقذنا ‘نظام التصميم’ من جحيم الفوضى البصرية؟

أشارككم قصة حقيقية من قلب المعركة البرمجية، كيف انتقلنا من فوضى الواجهات البصرية والتطبيقات غير المتناسقة إلى عالم من التناغم والكفاءة بفضل بناء "نظام تصميم"...

1 مايو، 2026 قراءة المزيد
الحوسبة السحابية

كانت خوادمنا تلتهم الميزانية: كيف أنقذتنا ‘الحوسبة بلا خوادم’ (Serverless) من جحيم الإدارة والتكاليف؟

أنا أبو عمر، وهذا سرد لتجربتي مع الانتقال إلى الحوسبة بلا خوادم (Serverless). سأشارككم كيف تخلصنا من كابوس إدارة الخوادم وتكاليفها الباهظة، وحولنا تركيزنا بالكامل...

1 مايو، 2026 قراءة المزيد
التوظيف وبناء الهوية التقنية

كنت أرتبك في المقابلات السلوكية: كيف أنقذني أسلوب STAR من جحيم الإجابات العشوائية؟

هل تشعر بالضياع والارتباك في المقابلات السلوكية؟ في هذه المقالة، أشارككم تجربتي الشخصية مع هذا الكابوس وكيف ساعدني أسلوب STAR البسيط في تنظيم أفكاري وتقديم...

1 مايو، 2026 قراءة المزيد
التوسع والأداء العالي والأحمال

كان فشل خدمة واحدة ينسف النظام بأكمله: كيف أنقذنا نمط ‘قاطع الدائرة’ (Circuit Breaker) من جحيم الفشل المتتالي؟

أشارككم قصة حقيقية من قلب المعركة البرمجية، كيف كاد نظامنا ينهار بسبب فشل خدمة صغيرة، وكيف كان نمط "قاطع الدائرة" (Circuit Breaker) هو طوق النجاة...

1 مايو، 2026 قراءة المزيد
البنية التحتية وإدارة السيرفرات

شبكة الخدمة (Service Mesh): طوق النجاة الذي أنقذنا من جحيم تتبع الأخطاء في الخدمات المصغرة

كان تتبع الطلبات المفقودة بين خدماتنا المصغرة كابوساً حقيقياً. في هذه المقالة، أسرد لكم قصة من واقع التجربة كيف أنقذتنا شبكة الخدمة (Service Mesh) من...

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