في هذا الموضوع سأشرح لكم تجربتي العملية في بناء محرك بحث قرآني متقدم سميته "باحث BAHETH" والذي يدمج بين سرعة الاسترجاع التي يوفرها محرك Elasticsearch، والقدرة الدلالية لنماذج Sentence Transformers المعتمدة على التعلم العميق.
جاءت فكرة هذا التطبيق من ضرورة توفر وسيلة بحث سريعة تفهم نصوص القرآن الكريم بدقة وعمق، فلا تكتفي بالمطابقة اللفظية بل تتجاوزها إلى فهم المعنى. فعند البحث عن كلمة النصر مثلًا، قد يرغب المستخدم في استعراض الآيات التي تتضمن الكلمة ذاتها أو مشتقاتها مثل ناصرين وينصرون، أو آيات تعبر عن المعنى نفسه مثل الفوز والغلبة.
هذا ما يقدمه تطبيق BAHETH من خلال دمج ثلاث مستويات من البحث هي البحث اللغوي والبحث الدلالي والبحث الهجين لتوفير نتائج بحث شاملة.

ما هو Elasticsearch
Elasticsearch هو محرك بحث وتحليل بيانات مفتوح المصدر مبني على مكتبة Apache Lucene. يتميز بقدرته على تخزين البيانات غير المهيكلة والبحث فيها وتحليلها بسرعة فائقة في الوقت الفعلي. وبفضل تصميمه الموزع يمكنه التعامل مع كميات هائلة من البيانات.
أبرز مميزات Elasticsearch
- السرعة والدقة في استرجاع النتائج مع ترتيبها حسب الصلة
- المرونة فهو لا يحتاج إلى مخطط بيانات ثابت مسبقًا ويسهل التعامل مع أنواع بيانات متعددة
- دعم لغات متعددة بما فيها العربية وإمكانية تخصيص المحللات اللغوية لمعالجة النصوص
- إجراء إحصاءات واستعلامات معقدة على البيانات المخزنة مثل عدها وتجميعها وتصنيفها
- العمل على خوادم موزعة لضمان السرعة والاستقرار مع زيادة حجم البيانات
- دعم البحث النصي المتقدم كالبحث عن الكلمات والعبارات والتطابق الجزئي
- السماح بتخزين النصوص كمتجهات رقمية لدعم البحث الدلالي السريع والدقيق
مفاهيم أساسية في Elasticsearch
لنتعرف معًا على أهم العناصر والمكونات الأساسية التي يعتمد عليها Elasticsearch لتخزين النصوص والبحث فيها بسرعة وكفاءة.
1. الفهرس Index
يشبه الفهرس قاعدة بيانات منطقية تجمع مجموعة من الوثائق المرتبطة بنفس السياق. في مشروع باحث، أنشأت فهرسًا باسم quran_search_v1 لتخزين آيات القرآن الكريم، فكل آية تخزن داخل هذا الفهرس، وتجهز بطريقة تسمح لاحقًا بالبحث السريع فيها سواء نصيًا أو دلاليًا.
2. المستند Document
المستند هو الوحدة الأساسية للبيانات في Elasticsearch، فكل سجل يمثل مستند واحد بصيغة JSON. على سبيل المثال تمثل كل آية من القرآن الكريم مستند مستقل يتضمن الآية نفسها ورقم السورة وحقل المتجه الدلالي للبحث كما في المثال التالي:
{
"sura": 24,
"aya": 35,
"text": "اللَّهُ نُورُ السَّمَاوَاتِ وَالْأَرْضِ...",
"sura_name": "النور",
"embedding": [0.231, -0.145, 0.562, ...]
}
3. الحقل Field
يحتوي كل مستند على عدة حقول، وكل حقل يمثل معلومة محددة على سبيل المثال في المستند السابق لدينا الحقول التالية:
- sura وaya: رقما السورة والآية
- text: نص الآية المستخدم للبحث النصي
- sura_name: اسم السورة
- embedding: التضمين أو التمثيل الرقمي للآية المستخدم في البحث الدلالي
4. المحللات اللغوية Analyzers
تمثل المحللات اللغوية سلسلة من العمليات المستخدمة لتحضير النصوص وتهيئتها للبحث، وهي ضرورية في النصوص العربية والقرآنية على وجه الخصوص للتعامل مع التشكيل واختلاف أشكال الحروف والجذور اللغوية حتى يظهر البحث النصي والدلالي بدقة، ومن أبرز مكوناتها
- Tokenizer: يقسم النص لوحدات صغيرة تسمى Tokens عادة ما تكون كلمات أو مصطلحات. على سبيل المثال يقسم نص الآية "اللَّهُ نُورُ السَّمَاوَاتِ" إلى ["اللَّهُ"، "نُورُ"، "السَّمَاوَاتِ"]
- Token Filters: يقوم بمعالجة الكلمات وتنظيفها مثل إزالة الكلمات الشائعة Stopwords (مثل من و على) وتوحيد أشكال الأحرف (مثل توحيد الهمزات وتحويل الألف المقصورة إلى ألف طويلة) واستخراج الجذور لتسهيل البحث عن الكلمات المشتقة.
5. التعيينات Mappings
تحدد التعيينات كيف يخزن Elasticsearch كل حقل في المستند وكيفية معالجته عند الفهرسة والبحث.
على سبيل المثال هذا كود Mapping مهمته تحديد كيفية تخزين حقل النص text في فهرس القرآن وكيفية تجهيزه للبحث بحيث يكون قابلًا للبحث الدلالي والبحث الدقيق معًا داخل Elasticsearch:
{
"properties": {
"text": {
"type": "text",
"analyzer": "arabic_text_analyzer",
"fields": {
"raw": { "type": "text", "analyzer": "arabic_keyword_analyzer" }
}
}
}
}
يُحلّل الحقل الرئيسي text باستخدام محلل arabic_text_analyzer ليسمح بالبحث المرن أي البحث بالكلمات والجذور والتشكيل، بينما يُحلّل الحقل الفرعي text.raw باستخدام arabic_keyword_analyzer الذي يحافظ على النص دون تحليل صرفي لضمان المطابقة الدقيقة.
6. الفهرس المقلوب Inverted Index
"يبني Elasticsearch فهرسًا مقلوبًا يربط كل مصطلح بقائمة معرفات المستندات التي تحتويه، مما يقلل الحاجة لمسح كامل المستندات. على سبيل المثال، عند البحث عن كلمة مثل "نور: سيوجه Elasticsearch الاستعلام مباشرة لقائمة المستندات التي تحتوي هذه الكلمة مما يسرع عملية البحث.
7. العناقيد Clusters والعقد Nodes
تنظم الخوادم في Elasticsearch على شكل عناقيد Clusters وعقد Nodes، المقصود بالعنقود Cluster مجموعة خوادم تعمل معًا كوحدة واحدة لتخزين البيانات وتنفيذ البحث. يوزع العنقود البيانات ويوازن الحمل بين العقد لضمان استقرار الأداء واستمرار الخدمة.
ملاحظة: في بيئة التطوير شغلت تطبيقي "باحث" على عقدة واحدة، ويمكن لاحقًا عند نشر المشروع توسيع البنية بإضافة عقد لتحسين الأداء.
تنفيذ البحث اللغوي

يعتمد البحث اللغوي على وجود الكلمات نفسها أو جذورها في النص، وينفذ وفق الخطوات التالية:
- تحليل النصوص عند الفهرسة ومعالجتها بحذف الكلمات الشائعة أو ما يعرف بكلمات التوقف Stop words، واستخلاص جذور الكلمات، وتقييس أو تطبيع الحروف Character Normalization كتوحيد الهمزات والألف المقصورة والطويلة..
- تحديد بنية الحقول بحيث يستخدم الحقل text الذي طبقت عليه معالجة كاملة للمطابقة المرنة والحقل text.raw الذي طبقت عليه معالجة التطبيع فقط للبحث الدقيق.
تمكين البحث متعدد الحقول مع أوزان مخصصة والتسامح مع الخطأ الإملائي فعند البحث يفحص المحرك عدة حقول لكل آية ويعطي كل حقل وزنًا مختلفًا حسب أهميته فالحقل المُعالَج بالكامل text له وزن أعلى لأنه يعكس النص بعد التحليل اللغوي الكامل بينما الحقل الخام text.raw له وزن متوسط للبحث الدقيق
يسمح المحرك بأخطاء إملائية صغيرة من خلال الخاصية fuzziness، ويختار أفضل الحقول التي تحقق أعلى تطابق بشرط أن تتطابق 75% من كلمات البحث على الأقل مع نصوص الفهرس.
يعتمد ترتيب النتائج بشكل افتراضي على حساب درجة التشابه باستخدام خوارزمية BM25 التي ترتيب النتائج حسب أهميتها بناء على عدة عوامل مثل:
ندرة الكلمة IDF: تمنح الكلمات النادرة درجة أعلى والشائعة درجة أقل
تكرار الكلمة في الآية TF: كلما تكررت الكلمة في المستند زادت درجة الصلة
نسبة طول الآية إلى متوسط طول كل الآيات DL/AvgDL: لتطبيع طول المستند بحيث لا تفضل الآيات الطويلة عن القصيرة
هذا البحث سريع ودقيق ويتعامل مع الأخطاء الإملائية والمشتقات اللغوية للكلمات لكنه لا يفهم المترادفات أو المعنى الضمني.
تنفيذ البحث الدلالي

يفيد البحث الدلالي في استرجاع النتائج بناء على المعنى والسياق وليس مجرد تطابق الكلمات، ويتم من خلال الخطوات التالية:
- تحويل النصوص إلى تمثيلات رقمية Embeddings فعند الفهرسة، كل تُحوّل آية إلى تمثيل عددي باستخدام نموذج paraphrase-multilingual-MiniLM-L12-v2 من مكتبة Sentence Transformers
- قبل التحويل نحتاج لتطبيع النص أيضًا كتوحيد الهمزات والألف المقصورة والطويلة وإزالة التشكيل ...إلخ. لتحسين جودة المتجهات.
- تخزن المتجهات في Elasticsearch في حقل dense_vector داخل كل مستند مع تفعيل الفهرسة للبحث السريع ويستخدم مقياس تشابه جيب التمام Cosine Similarity لقياس درجة القرب بين المتجهات بحيث يعطي قيمة بين 0 عند عدم تطابق و1 عند التطابق الكامل.
- عند البحث الدلالي عن كلمة أو عبارة تحول لمتجه رقمي بنفس طريقة الآيات ويجري البحث باستخدام خوارزمية الجار الأقرب k-Nearest Neighbors أو kNN اختصارًا.
ملاحظة: قد لا تدعم بعض إصدارات Elasticsearch القديمة قياس التشابه بين المتجهات باستخدام kNN، في هذه الحالة يمكن استخدام طريقة بديلة لحساب التشابه بين استعلام المستخدم ومتجهات الآيات لضمان استمرار عمل البحث الدلالي.هذا البحث يفهم المترادفات والسياق لكنه لا يشترط المطابقة الحرفية للكلمات. كما أن النموذج المستخدم عام وليس مدرَّبًا على النص القرآني تحديدًا، مما يحد من دقته في فهم الدلالات السياقية الخاصة.
تنفيذ البحث الهجين

يجمع البحث الهجين بين نقاط قوة البحث اللغوي والدلالي لتقديم أفضل النتائج الممكنة، مستفيدًا من دقة المطابقة اللغوية وعمق الفهم الدلالي. يجري تنفيذ بحثين منفصلين بحث لغوي وبحث دلالي بشكل منفصل ثم تجمع نتائج البحث اللغوي والدلالي. وتطبق عملية تقييس Normalization على درجات كل نوع من البحث لجعلها قابلة للمقارنة وتحسب درجة تقييم نهائية لكل مستند بناءً على مزيج من نتيجة البحث النصي ونتيجة البحث الدلالي، مع مراعاة وزن كل نوع منهما في الحساب. أخيرًا ترتب جميع الوثائق المدمجة بناءً على درجة التقييم وتعرض النتائج الأعلى صلة.
الحصول على مصدر بيانات قرآني للتطبيق
اعتمدت في هذا التطبيق على تحميل نص القرآن الكريم من مشروع تنزيل Tanzil، والذي وفر لي النص بتنسيق XML (اسم الملف quran-simple-plain.xml) وقد حولته بعد ذلك إلى صيغة CSV من خلال كود برمجي مكتوب بلغة بايثون لأن هذا التنسيق كان أسهل لي في التعامل والفهرسة.
وضعت الكود المسؤول عن عملية التحويل معرف ضمن ملف باسم prepare_quran_data.py في مجلد المشروع ومهمته الأساسية هي استخراج جميع السور والآيات من ملف XML للقرآن الكريم وتنظيمها في جدول بيانات ثم حفظها في ملف CSV منسق يحتوي على رقم السورة، رقم الآية، اسم السورة، ونص الآية:
import xml.etree.ElementTree as ET
import pandas as pd
import re
def remove_arabic_diacritics(text):
# إزالة التشكيل من النص العربي
if not text:
return ""
return re.sub(r"[\u064b-\u0652\u0670]", "", text)
def generate_csv_from_xml(
xml_file_path="data/quran-simple-plain.xml", output_csv_path="quran_data.csv"
):
tree = ET.parse(xml_file_path)
root = tree.getroot()
records = []
for sura in root.findall("sura"):
sura_index = int(sura.get("index"))
sura_name = sura.get("name")
for aya in sura.findall("aya"):
aya_index = int(aya.get("index"))
text = aya.get("text")
bismillah = aya.get(
"bismillah", ""
) # بعض السور تبدأ بـ "بسم الله الرحمن الرحيم"
record = {
"sura": sura_index,
"aya": aya_index,
"text": text,
"sura_name": sura_name,
# "text_no_diacritics": remove_arabic_diacritics(text), # إذا كنت تريد النص بدون تشكيل
# "translation_en": "...", # هنا يمكن جلب الترجمة الإنجليزية
}
records.append(record)
df = pd.DataFrame(records)
df.to_csv(output_csv_path, index=False, encoding="utf-8-sig")
print(f" تم إنشاء ملف CSV بنجاح في: {output_csv_path}")
if __name__ == "__main__":
generate_csv_from_xml()
اكتفيت باستخراد البيانات الأساسية من الملف ويمكن استخراج بيانات أخرى عند الحاجة، ويكفي تنفيذ الملف مرة واحدة قبل تشغيل التطبيق لتحويل صيغة البيانات إلى CSV في حال لم تكن متوفرة لديك، أما في حال توفرها بهذه الصيغة فلا داعي لتنفيذ هذا الكود.
بعد تجهيز البيانات، يتم إنشاء الفهرس، وفهرسة جميع الآيات مع توليد المتجهات الدلالية لها. وبعد تجهيز الفهرس، يتم تطبيق المحلل اللغوي arabic_text_analyzer للبحث العام، والمحلل اللغوي arabic_keyword_analyzer للبحث الدقيق، بالإضافة إلى تطبيق التعيينات Mappings على الحقول لضمان تخزين كل عنصر بشكل مناسب.
الكود البرمجي الكامل لتطبيق الباحث القرآني BAHETH
يمكن تحميل الكود الكامل للمشروع من مستودع GitHub.
لن أغوص في تفاصيل الكود البرمجي لكل ملف، وسأوفر لكم قائمة بأهم الملفات التي يتضمنها المشروع ودور كل منها بإيجاز ويمكنكم تحميل المشروع لديكم ومطالعة كل التفاصيل التقنية:
- prepare_quran_data.py: يحوّل ملف القرآن الكريم من تنسيق XML إلى CSV لتجهيزه للفهرسة.
- index_mapping.py: يعرف إعدادات الفهرس في Elasticsearch، بما في ذلك المحللات اللغوية Analyzers وتعيينات الحقول Mappings.
- setup_index.py: يشغل وظيفة إنشاء الفهرس وفهرسة جميع بيانات القرآن دفعة واحدة.
- quran_search_backend.py: يحتوي على وظائف الاتصال بمحرك البحث، وإنشاء الفهرس، وتوليد التضمينات أو المتجهات الدلالية Embeddings وتنفيذ عمليات البحث.
- app.py: يمثل الواجهة الخلفية للتطبيق Flask يستقبل طلبات البحث من المستخدم، ثم يمررها إلى quran_search_backend.py ويعيد النتائج
- templates/index.htm: ملف الواجهة الأمامية يتيح إدخال عبارة البحث، وتحديد نوع البحث لغوي أو دلالي أو هجين وعدد النتائج ثم يعرضها بطريقة مرتبة.
تلك كانت خلاصة تجربتي الأولى في بناء تطبيق محرك بحث قرآني، وفرت من خلالها نموذجًا يدمج بين البحث النصي والدلالي في القرآن بالاستفادة من قدرات محرك Elasticsearch وتقنيات التعلم العميق، وأرجو أن تكون بمثابة خطوة أولى نحو تطوير أنظمة بحث قرآنية أكثر تقدمًا.
أدعوكم للاطلاع على الكود البرمجي لتطبيق باحث BAHETH وتجربة التطبيق لديكم، واقتراح أفكار من شأنها تحسينه وزيادة دقته.