اعمل بحث داخلي سريع للوثائق باستخدام Meilisearch
هتطلع من المقال ده ببحث داخلي للوثائق يشتغل محليًا في أقل من ساعة، ويرد في عشرات المللي ثانية بدل انتظار استعلامات LIKE الثقيلة.
مستوى القارئ: متوسط
المشكلة باختصار
لو عندك مركز مساعدة، توثيق داخلي، أو dashboard فيها مئات المقالات، أول حل بييجي في بالك غالبًا هو البحث داخل قاعدة البيانات بـ LIKE '%keyword%'. الطريقة دي بتفشل لما عدد الوثائق يزيد، ولما المستخدم يكتب كلمة ناقصة أو فيها typo.
الافتراض هنا إن عندك 1,000 إلى 50,000 وثيقة نصية، وكل وثيقة فيها عنوان، محتوى، قسم، ورابط. في الحالة دي Meilisearch اختيار عملي لأنه محرك بحث جاهز، سريع، وبيتعامل مع typo tolerance وranking rules بدون ما تبني كل ده بنفسك.
الفكرة بمثال واضح
ركز في المثال ده. عندك صفحة اسمها “سياسة الفواتير المتأخرة”، والمستخدم كتب: overdue invocie بدل overdue invoice. بحث SQL التقليدي ممكن يرجع صفر نتائج. Meilisearch غالبًا يرجع الصفحة الصح لأنه عنده typo tolerance وقواعد ترتيب مبنية للبحث النصي.
اللي بيحصل فعلاً إن التطبيق لا يدور في قاعدة البيانات الأصلية مع كل ضغطة. بدل ما يعمل كده، التطبيق يرسل نسخة صغيرة من الوثائق إلى index في Meilisearch. وقت البحث، التطبيق يسأل Meilisearch ويرجع النتائج للمستخدم، والقاعدة الأصلية تفضل مصدر الحقيقة للبيانات.
الخطوات العملية
أفضل طريقة تبدأ بيها هي Docker. مش محتاج cluster ولا إعدادات معقدة. شغّل Meilisearch محليًا، ضيف 3 وثائق، واضبط الحقول القابلة للبحث والتصفية.
# 1) شغّل Meilisearch محليًا
mkdir docs-search-demo
cd docs-search-demo
docker run -it --rm \
-p 7700:7700 \
-e MEILI_MASTER_KEY='local_master_key_123456' \
-v "$PWD/meili_data:/meili_data" \
getmeili/meilisearch:latest
افتح terminal تاني ونفّذ الطلبات دي:
# 2) أنشئ index اسمه docs
curl -X POST 'http://localhost:7700/indexes' \
-H 'Authorization: Bearer local_master_key_123456' \
-H 'Content-Type: application/json' \
--data-binary '{"uid":"docs","primaryKey":"id"}'
# 3) اضبط الحقول المهمة قبل إضافة الوثائق
curl -X PATCH 'http://localhost:7700/indexes/docs/settings' \
-H 'Authorization: Bearer local_master_key_123456' \
-H 'Content-Type: application/json' \
--data-binary '{
"searchableAttributes": ["title", "body"],
"displayedAttributes": ["id", "title", "section", "url"],
"filterableAttributes": ["section"]
}'
# 4) أضف وثائق تجريبية
curl -X POST 'http://localhost:7700/indexes/docs/documents' \
-H 'Authorization: Bearer local_master_key_123456' \
-H 'Content-Type: application/json' \
--data-binary '[
{"id":"doc_1","title":"سياسة الفواتير المتأخرة","body":"خطوات التعامل مع overdue invoices وتنبيه العميل.","section":"finance","url":"/docs/late-invoices"},
{"id":"doc_2","title":"إعداد Webhook للدفع","body":"شرح ربط Stripe webhook وتسجيل محاولات الدفع.","section":"payments","url":"/docs/payment-webhook"},
{"id":"doc_3","title":"استرجاع كلمة المرور","body":"إرسال رابط reset password والتحقق من انتهاء الصلاحية.","section":"auth","url":"/docs/reset-password"}
]'
# 5) جرّب بحث فيه typo
curl -X POST 'http://localhost:7700/indexes/docs/search' \
-H 'Authorization: Bearer local_master_key_123456' \
-H 'Content-Type: application/json' \
--data-binary '{"q":"overdue invocie","filter":"section = finance","limit":5}'
النتيجة المتوقعة إن الوثيقة الأولى ترجع ضمن النتائج. لو ما رجعتش، راجع قيمة searchableAttributes وتأكد إن مهمة إضافة الوثائق خلصت بنجاح.
القياس قبل وبعد
في سيناريو واقعي، موقع دعم فيه 12,000 وثيقة كان بحث LIKE على عنوان ومحتوى بياخد حوالي 850ms عند P95. بعد نقل البحث إلى Meilisearch، نفس الاستعلامات بقت بين 35 و60ms على جهاز صغير بذاكرة 2GB. الرقم هنا تقديري، لكنه قريب من اللي هتشوفه لو الوثائق نصية وحجمها متوسط.
استخدم قياس بسيط بدل الانطباع:
# قياس 20 طلب بحث وحساب الزمن الإجمالي التقريبي
for i in {1..20}; do
curl -s -o /dev/null -w "%{time_total}\n" \
-X POST 'http://localhost:7700/indexes/docs/search' \
-H 'Authorization: Bearer local_master_key_123456' \
-H 'Content-Type: application/json' \
--data-binary '{"q":"invoice","limit":5}'
done
الـ trade-off هنا
بتكسب سرعة بحث وتجربة أفضل مع الأخطاء الإملائية وترتيب نتائج أوضح. بتخسر بساطة النظام، لأن عندك خدمة إضافية لازم تراقبها وتعملها backup وتزامن بينها وبين قاعدة البيانات الأصلية.
لو الوثيقة اتعدلت في PostgreSQL أو MySQL، لازم تبعت التعديل إلى Meilisearch. أسهل حل في البداية: job بعد الحفظ يرسل النسخة الجديدة. لما تكبر، استخدم queue مثل BullMQ أو Sidekiq عشان التحديثات ما تعطلش request المستخدم.
متى لا تستخدم هذه الطريقة
ما تستخدمش Meilisearch لو عندك 200 صفحة فقط والبحث الحالي سريع. برضه ما تستخدموش لو محتاج semantic search عميق بالإيمبدنجز؛ هنا هتحتاج vector database أو ميزة vector search حسب المتطلبات. ولو عندك بيانات حساسة جدًا، راجع مفاتيح الوصول والـ deployment قبل ما تفتح محرك البحث على الإنترنت.
مصادر مهمة
- Meilisearch Docker guide
- Meilisearch documents API
- Meilisearch settings API
- Displayed and searchable attributes
الخطوة التالية
افتح مشروع فيه صفحة بحث بطيئة، وشغّل Meilisearch محليًا على 100 وثيقة حقيقية. لو P95 نزل تحت 100ms، انقل التزامن إلى background job قبل ما تفكر في production.