استخدام خريطة ومجموعة لبناء مُفهرِس (Indexer) عالي الكفاءة
مقدمة
ينطلق نجاح أيّ نظام بحث أو محرك توصية من قدرة بنيته الأساسية على فهرسة البيانات بسرعة ودقّة. ولأنّ حجم المحتوى الرقمي يتضاعف بوتيرة متسارعة، بات من الضروري اعتماد هياكل بيانات تُوازن بين استهلاك الذاكرة وزمن الوصول. تمثّل الخريطة (Map) والمجموعة (Set) حجرَي الزاوية في هذا السياق؛ فالأولى تربط المفاتيح بالقيم، بينما تُوفّر الثانية تمييزًا فوريًّا لوجود عنصر من عدمه. يجمع هذا المقال بين الشرح النظري المتعمّق والتطبيق العملي، موضحًا كيف يُمكن دمج هاتين البنيتين لبناء مُفهرِس مرن وقابل للتوسع، مع مراعاة معايير تحسين محركات البحث (SEO) وممارسات كتابة الكود النظيفة.
أولًا: لمحة سريعة عن المفهرسة وأهميتها
تُعرَّف المفهرسة بأنّها العملية التي يتمّ فيها تحويل البيانات الخام—سواء كانت نصوصًا، صورًا أو سجلات مهيكلة—إلى بنية تسمح بالاسترجاع الفوري للمعلومات. عندما يُنقِّب المستخدم في قاعدة بيانات ضخمة، يتفاعل في الحقيقة مع طبقة فهرسة مخفية تنظِّم المحتوى وتربط الاستعلامات بالمستندات ذات الصلة. وبغياب هذه الطبقة، يُصبح البحث خطّيًّا (Linear Search)، أي بزمن يتناسب طرديًّا مع حجم البيانات، ما يؤدي إلى اختناق الأداء.
ثانيًا: الخريطة (Map) – القلب النابض للمفهرس
1. تعريف الخريطة
الخريطة هي بنية بيانات تربط كل مفتاح بقيمة. في معظم لغات البرمجة الحديثة تُعرَف بأسماء مختلفة مثل HashMap في Java، Dictionary في Python، أو Object/Map في JavaScript. تتميّز بزمن وصول شبه ثابت O(1) في المتوسط بفضل الاعتماد على الدوال التجزئة (Hash Functions).
2. خصائص تجعل الخريطة مثالية للفهرسة
-
تخزين المتكررات: المفتاح يُمثّل عادةً كلمة أو معرفًا فريدًا، بينما تحتضن القيمة قائمة مستندات تظهر بها تلك الكلمة.
-
تحديث فوري: إضافة أو إزالة مستند يقتصر على تعديل قيمة مفتاح محدد، دون الحاجة إلى إعادة بناء الهيكل كاملًا.
-
التوسع الأفقي: يمكن تجزئة الخريطة (Sharding) على عُقد متعددة بفضل توزيع المفاتيح بالتجزئة.
3. اختيار دالة التجزئة
دقّة المفهرس تعتمد بشدّة على جودة دالة التجزئة. الدالة المثالية تُوزّع المفاتيح بالتساوي لتجنّب التصادمات، ما يضمن زمن وصول متجانسًا. يُوصى بخوارزميات مثل MurmurHash أو CityHash بفضل سرعتها وقِلة معدل التصادم.
ثالثًا: المجموعة (Set) – الحارس الأمين لسلامة البيانات
1. تعريف المجموعة
المجموعة تحوي عناصر فريدة بلا تكرار، وتقدِّم عمليات إضافة وفحص وحذف بعقدية زمنية شبه ثابتة. يستند تنفيذها الداخلي عادةً إلى نفس آلية التجزئة المستخدمة في الخريطة.
2. دور المجموعة في المفهرس
-
منع التكرار: أثناء بناء الفهرس، قد تظهر الكلمة نفسها مرات عديدة داخل المستند. استخدام مجموعة لتجميع معرِّفات المستندات يمنع تكرار الإسناد.
-
التقاطع والاتحاد: عند تنفيذ استعلامات مركبة (AND/OR)، تُسهِّل المجموعة إجراء العمليات المنطقية على مجموعات المستندات.
رابعًا: تصميم المفهرس باستخدام Map وSet
1. البنية الأساسية
javascriptMap<String, Set<DocumentID>>
-
المفتاح (String): الرمزة النصية (Token) أو الكلمة المفتاحية.
-
القيمة (Set
) : مجموعة معرِّفات المستندات التي تحتوي على تلك الكلمة.
2. خوارزمية البناء
-
استخراج الرموز (Tokenization): تقسيم النص إلى كلمات، مع تحويلها إلى حالة موحدة (Lowercase) واستبعاد علامات الترقيم.
-
الإسناد إلى الخريطة:
-
إذا لم يوجد المفتاح، تُنشأ مجموعة جديدة ويُضاف معرّف المستند.
-
إذا وُجد، يُضاف المعرف إلى المجموعة المرتبطة إن لم يكن موجودًا.
-
-
تكرار العملية لكل مستند حتى استهلاك مجموعة البيانات كاملة.
3. استرجاع النتائج
-
عند إجراء بحث بكلمة واحدة، تُعاد مجموعة المستندات مباشرة.
-
للبحث المركب (مثل “الذكاء” AND “الاصطناعي”)، يُطبَّق تقاطع للمجموعتين.
-
عند استخدام عامل OR يُجرى اتحاد للمجموعتين.
خامسًا: تحسين الأداء والذاكرة
| الجانب | التحدي | الحل عبر Map & Set | الملاحظات |
|---|---|---|---|
| الذاكرة | التكرار العالي للرموز | استخدام Set لتفادي تكرار معرّفات المستندات | يقلّل استهلاك الذاكرة بنسبة قد تتجاوز 40 ٪ |
| الزمن | تصادم التجزئة | اختيار دوال تجزئة فعالة وتعديل حجم الجداول (Rehash) | يحافظ على عقدية شبه ثابتة |
| التوسع | ازدياد حجم البيانات | تجزئة الخريطة إلى شرائح (Shards) حسب النطاق التجزئي | يسمح بالتوازي |
| التحديث | إدراج مستندات جديدة باستمرار | عمليات الإضافة في Map وSet تُنفَّذ في O(1) | يدعم الأنظمة الزمنية الحية (Real‑time) |
سادسًا: مثال تطبيقي بلغة JavaScript
javascriptclass Indexer {
constructor() {
this.index = new Map(); // Map>
}
addDocument(docId, text) {
const tokens = text.toLowerCase()
.replace(/[^\p{L}\p{N}\s]+/gu, '')
.split(/\s+/);
for (const token of tokens) {
if (!token) continue; // تجاوز الرموز الفارغة
if (!this.index.has(token)) {
this.index.set(token, new Set());
}
this.index.get(token).add(docId);
}
}
search(...queryTokens) {
const results = queryTokens
.map(t => this.index.get(t.toLowerCase()) || new Set())
.reduce((a, b) => new Set([...a].filter(x => b.has(x))));
return Array.from(results);
}
}
يُظهر المثال كيف تُنشأ خريطة مرتبطة بمجموعات لحفظ فهرس معكوس مبسط، مع دعم عمليتَي الإضافة والبحث بتقاطعات AND.
سابعًا: الاعتبارات الأمنية
-
تجنب هجمات رفض الخدمة: يجب وضع حد أقصى لطول المفتاح وعدد المستندات في المجموعة.
-
تطبيع البيانات: إزالة الرموز الضارة أو تسلسل الحروف الذي قد يؤدي إلى هجمات إدخال (Injection).
-
صيانة دورية: تنفيذ Job يجمع المفاتيح غير المستخدمة ويحرّر الذاكرة.
ثامنًا: دمج المفهرس في نظام بحث شامل
-
طبقة التخزين: يمكن حفظ Map بشكل تسلسُلي عبر قواعد NoSQL مثل Redis أو MongoDB لتحقيق سرعة أعلى.
-
طبقة الخدمة: REST API تستقبل الاستعلامات وتستدعي عمليات التقاطع أو الاتحاد.
-
طبقة التقديم: واجهة مستخدم تعرض النتائج مع إبراز (Highlight) الكلمات المطابقة.
تاسعًا: توسيع المفهرس لدعم مزايا متقدمة
-
ترتيب النتائج (Ranking): إضافة وزن (TF‑IDF) لكل زوج كلمة/مستند داخل الخريطة بدل المجموعة.
-
التصحيح الإملائي: استخدام Map أخرى تخزن متغيرات الكلمات (N‑grams) لاقتراح بدائل.
-
الفهرسة متعددة اللغات: إنشاء شظايا منفصلة لكل لغة أو توحيد الرموز باستعمال مخططات ترميز Unicode معيارية.
عاشرًا: الخلاصة
يُعد الجمع بين خريطة ومجموعة طريقة مثالية لبناء مُفهرِس بسيط وسريع يتناسب مع تطبيقات البحث الصغيرة والمتوسطة، وبمزيد من التحسينات—كالترتيب اللحظي للنتائج والتوزيع الأفقي—يمكنه ترقية الأداء إلى مستوى ينافس حلول الفهرسة الضخمة. إنّ فهم المبادئ الأساسية للخريطة والمجموعة يفتح الباب أمام تصميم بُنى بيانات متخصِّصة تلبي احتياجات منظومات المحتوى الحديثة التي تتطلّب استجابة شبه آنية واستيعابًا كبيرًا للبيانات.
المراجع
-
Goodrich, Michael & Tamassia, Roberto. Data Structures and Algorithms in Java. Wiley, 2014.
-
Wajid, Mohiuddin & et al. “Survey on Inverted Index Construction Techniques.” International Journal of Computer Applications, vol. 182, no. 7, 2018.

