الرئيسيةمن أناالدوراتالمدونةسوق الأوامرالمناهج والباقاتالشركاء

دورات عربية متخصصة في التقنية والبرمجة والذكاء الاصطناعي.

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

المنصة

  • الرئيسية
  • من أنا
  • الدورات
  • المناهج والباقات
  • سوق الأوامر
  • المدونة

الدعم

  • الأسئلة الشائعة
  • تواصل معنا
  • سياسة الخصوصية
  • شروط استخدام التطبيق
  • سياسة الاسترجاع

© 2026 أحمد حايس. جميع الحقوق محفوظة.

الرئيسيةالدوراتالمناهجالمدونةالدخول
How To Make It

ابنِ بحثًا عربيًا كاملًا في PostgreSQL بدون Elasticsearch

متوسط29 يوليو 20265 دقائق قراءة
ابنِ بحثًا عربيًا كاملًا في PostgreSQL بدون Elasticsearch

المستوى: متوسط. الدليل ده يفترض إنك تعرف أساسيات SQL وعندك جدول PostgreSQL شغّال (الإصدار 13 أو أحدث).

هتخرج من المقال ده وعندك بحث عربي كامل جوّه PostgreSQL يرجّع النتيجة في أقل من 10 مللي ثانية على ملايين الصفوف، من غير ما تركّب Elasticsearch ولا تدفع سيرفر زيادة.

بحث نصي عربي كامل في PostgreSQL بدون Elasticsearch

المشكلة باختصار

أغلب المطورين بيعملوا البحث بـ ILIKE '%كلمة%'. الطريقة دي بتفشل على مقياس حقيقي. مع كل استعلام، PostgreSQL بيقرأ كل صف في الجدول (Sequential Scan). على جدول بـ 2 مليون منتج، ده معناه مئات المللي ثانية لكل بحث، وبيزيد كل ما البيانات تكبر. وكمان ILIKE مبيرتّبش النتائج بالصلة، فالنتيجة الأهم ممكن تطلع في الآخر.

خوادم قواعد بيانات PostgreSQL في مركز بيانات ترمز لبناء بحث نصي عربي سريع

الفكرة أولًا: فهرس آخر الكتاب

تخيّل كتاب 900 صفحة وعايز كل صفحة فيها كلمة "التصميم". قدامك طريقتين. الأولى: تقلّب الكتاب صفحة صفحة من الأول للآخر. دي بطيئة، وهي بالظبط اللي بيعملها ILIKE. الطريقة التانية: تفتح فهرس آخر الكتاب، تلاقي كلمة "التصميم" وجنبها أرقام الصفحات على طول.

ده بالظبط اسمه الفهرس المعكوس (Inverted Index)، وهو أساس أي محرك بحث. بدل ما يدوّر في كل النص، بيبني قايمة مقلوبة: كل كلمة وجنبها الصفوف اللي فيها.

علميًا: PostgreSQL بيحوّل النص لنوع بيانات اسمه tsvector. الـ tsvector بيقطّع الجملة لكلمات (tokens)، بيشيل الكلمات الشائعة (زي "في" و"من")، وبيرجّع الكلمة لأصلها (تجذير). بعد كده فهرس GIN بيربط كل كلمة بالصفوف اللي فيها، فالبحث بيبقى قفزة مباشرة مش مسح كامل.

رفوف مكتبة منظمة كتشبيه للفهرس المعكوس الذي يربط كل كلمة بصفوفها في قاعدة البيانات

الخطوات: من عمود نصي لبحث مفهرس

الافتراض إن عندك جدول منتجات بالشكل ده: عمود title وعمود body فيهم نص عربي.

1) ضيف عمود tsvector محسوب تلقائيًا

بدل ما تحدّث الفهرس بإيدك، خلّي PostgreSQL يحسبه لوحده في عمود مخزّن:

SQL
ALTER TABLE products
  ADD COLUMN search_vec tsvector
  GENERATED ALWAYS AS (
    to_tsvector('arabic', coalesce(title,'') || ' ' || coalesce(body,''))
  ) STORED;

العمود ده بيتحدّث لوحده مع أي INSERT أو UPDATE، فمفيش داعي لـ trigger يدوي. لو PostgreSQL عندك أقدم من 12، استخدم trigger بدل الـ generated column.

2) اعمل فهرس GIN

SQL
CREATE INDEX products_search_idx
  ON products USING gin(search_vec);

GIN هو الفهرس الموصى به رسميًا للبحث النصي الكامل في PostgreSQL. من غيره، الاستعلام هيفضل يمسح الجدول كله حتى لو العمود موجود.

3) اكتب الاستعلام والترتيب

SQL
SELECT id, title,
       ts_rank(search_vec, q) AS rank
FROM products, websearch_to_tsquery('arabic', 'مطاعم بحرية رخيصة') AS q
WHERE search_vec @@ q
ORDER BY rank DESC
LIMIT 20;

الدالة websearch_to_tsquery بتفهم كتابة المستخدم العادية زي ما بيكتب في جوجل، فمش محتاج تنضّف مدخلاته. والعلامة @@ معناها "هل المستند مطابق للاستعلام". وts_rank بيدّي درجة صلة لكل صف علشان الأهم يطلع الأول.

الأرقام: قبل وبعد

على جدول تجريبي بـ 2 مليون صف (أرقام تقديرية من EXPLAIN ANALYZE):

  • بدون فهرس (Seq Scan): حوالي 850 مللي ثانية لكل بحث.
  • بفهرس GIN (Bitmap Index Scan): حوالي 9 مللي ثانية.

ده تحسّن حوالي 90 ضعف على نفس البيانات ونفس السيرفر. السيناريو الواقعي: متجر إلكتروني بـ 40 ألف زيارة/يوم، البحث كان بياخد ثانية وقت الذروة وبيضغط على قاعدة البيانات؛ بعد الفهرس بقى شبه فوري والحِمل نزل بشكل واضح.

الـ trade-offs اللي لازم تعرفها

بتكسب سرعة، بتخسر مساحة وكتابة. فهرس GIN بياخد مساحة إضافية (ممكن توصل 30 إلى 50% من حجم العمود النصي)، وبيخلّي الـ INSERT وUPDATE أبطأ شوية لأنه بيحدّث الفهرس مع كل تعديل. الافتراض هنا إن القراءة عندك أكتر من الكتابة بكتير، وده صحيح في أغلب تطبيقات البحث.

نقطة مهمة في العربي: إعداد 'arabic' بيعمل تجذير وبيشيل كلمات الوقف، لكنه مبيوحّدش كل صور الهمزة والتشكيل. لو محتاج "إسلام" و"اسلام" يتطابقوا، طبّع النص قبل الفهرسة: وحّد الألف وشيل التشكيل. الـ trade-off هنا إنك بتكسب استدعاء أعلى (recall) مقابل خطوة معالجة زيادة.

متى لا تستخدم هذه الطريقة

لو محتاج بحث ضبابي على أخطاء إملائية أو جزء من كلمة (زي "مطع" توصل لـ "مطعم")، الـ Full Text Search مش الأداة الصح لأنها بتطابق كلمات كاملة مجذّرة؛ استخدم امتداد pg_trgm. ولو عندك مليارات المستندات وبحث موزّع وترتيب معقّد جدًا، ساعتها Elasticsearch بيدفع تكلفته. لكن تحت مليون لـ عشرة ملايين صف، بحث PostgreSQL كفاية وأبسط في التشغيل.

التحقق من أنه يعمل

شغّل الاستعلام جوّه EXPLAIN ANALYZE وبُص على نوع المسح:

SQL
EXPLAIN ANALYZE
SELECT id FROM products
WHERE search_vec @@ websearch_to_tsquery('arabic', 'مطعم بحري');

لو شفت Bitmap Index Scan on products_search_idx يبقى الفهرس شغّال. لو شفت Seq Scan، غالبًا الإعداد اللغوي في الاستعلام مختلف عن اللي في العمود المحسوب — لازم الاتنين يكونوا بنفس الإعداد 'arabic'، وإلا PostgreSQL مش هيستخدم الفهرس.

الخطوة التالية

افتح أكبر جدول عندك فيه نص عربي، وضيف عليه عمود search_vec المحسوب وفهرس GIN بس. بعدين شغّل بحثك القديم داخل EXPLAIN ANALYZE قبل وبعد، وقارن الزمن. لو نزل من مئات المللي ثانية لأرقام أحادية، الفهرس شغّال صح.

المصادر

  • PostgreSQL Documentation — Chapter 12: Full Text Search: https://www.postgresql.org/docs/current/textsearch.html
  • PostgreSQL Documentation — 12.9 GIN and GiST Index Types: https://www.postgresql.org/docs/current/textsearch-indexes.html
  • PostgreSQL Documentation — 12.3 Controlling Text Search (ts_rank و websearch_to_tsquery): https://www.postgresql.org/docs/current/textsearch-controls.html
  • PostgreSQL Documentation — Generated Columns: https://www.postgresql.org/docs/current/ddl-generated-columns.html
  • Snowball Arabic Stemmer (المدمج في PostgreSQL 13+): https://snowballstem.org/algorithms/arabic/stemmer.html
  • PostgreSQL Documentation — pg_trgm (للبحث الضبابي وجزء الكلمة): https://www.postgresql.org/docs/current/pgtrgm.html

هل استفدت من المقال؟

اطّلع على المزيد من المقالات والدروس المجانية من نفس المسار المعرفي.

تصفّح المدونة