ليش البحث العربي في SQLite ما يلقى الكلمة، وكيف تصلحه
5 أكتوبر 2026 · كل النتائج تحت مجرّبة فعليًا على SQLite 3.50.4
المشكلة في سطرين
عندك تطبيق ملاحظات أو رسائل يحفظ النصوص في SQLite، والمستخدم يكتب «مذكرة» في البحث. النص موجود قدامه بأكثر من شكل: «المُذَكِّرَة» و«للمذكرة» و«والمذكره». البحث العادي يرجّع له جزء منها بس، أو ما يرجّع شي.
جرّبنا هالأربع جمل في جدول SQLite:
- لازم نكتب مذكرة تفاهم
- راجعت المُذَكِّرَة أمس
- أضفت للمذكرة ملاحظات
- والمذكره ضاعت
١) LIKE: يفوّت التشكيل واختلاف الكتابة، ويمر على كل الصفوف
SELECT body FROM notes WHERE body LIKE '%مذكرة%';
النتيجة: «لازم نكتب مذكرة» و«أضفت للمذكرة» بس. فوّت «المُذَكِّرَة» لأن التشكيل حروف زيادة بين الحروف، وفوّت «والمذكره» لأن الهاء غير التاء المربوطة.
وفيه مشكلتين ثانيتين: LIKE '%علم%' يطابق داخل كلمات ما لها علاقة مثل «المعلم» و«العلمي». وEXPLAIN QUERY PLAN يقول SCAN، يعني يقرأ كل صف في الجدول مع كل بحث، فيبطأ كل ما كبرت البيانات.
٢) FTS5: سريع، لكنه يطابق الكلمة كاملة بس
CREATE VIRTUAL TABLE notes_fts USING fts5(body, tokenize='unicode61'); SELECT body FROM notes_fts WHERE notes_fts MATCH '"مذكرة"';
FTS5 يستخدم فهرس، فهو سريع حتى مع مئات الآلاف من الصفوف. لكنه يقسم النص لكلمات ويطابق الكلمة كما هي: النتيجة «لازم نكتب مذكرة» بس. «للمذكرة» عنده كلمة ثانية، و«المُذَكِّرَة» المشكّلة كلمة ثالثة.
وحتى البحث ببداية الكلمة ("مذكرة"*) ما يفيد، لأن السابقة في أول الكلمة مو في آخرها. وخيار remove_diacritics 2 في unicode61 ما يشيل التشكيل العربي: جرّبناه، و«المذكرة» ما لقت «المُذَكِّرَة».
الأسباب الثلاثة
| السبب | مثال | LIKE | FTS5 |
|---|---|---|---|
| التشكيل والتطويل | مُذَكِّرَة، كتـــاب | يفوّت | يفوّت |
| أكثر من كتابة للحرف | أ إ آ ا، ة ه، ى ي، ؤ و | يفوّت | يفوّت |
| السوابق الملتصقة | ال، لل، بال، و، ب | يلقى، مع تطابقات غلط | يفوّت |
الحل الأول: وحّد النص (مجاني)
شيل التشكيل والتطويل، ووحّد «أ إ آ ٱ» إلى «ا»، و«ى ئ» إلى «ي»، و«ة» إلى «ه»، و«ؤ» إلى «و». وطبّقها على الطرفين: النص اللي تحفظه، والكلمة اللي يبحث عنها المستخدم.
pip install https://github.com/musaed300/arabic-search-lite/releases/download/v1.0.0/arabic_search_lite-1.0.0-py3-none-any.whl
from arabic_search_lite import normalize
normalize("المُذَكِّرَة") # "المذكره"
احفظ نسخة موحّدة من النص في عمود ثاني، وابحث فيه بالكلمة بعد توحيدها. هالخطوة لحالها تصلّح أول سببين. هذي مكتبة arabic-search-lite المجانية برخصة MIT، لـPython وJavaScript.
بس السبب الثالث يظل: مع FTS5 «للمذكره» تبقى كلمة غير «مذكره»، ومع LIKE ترجع التطابقات الغلط والمرور على كل الصفوف.
الحل الثاني: FTS5 مع فصل السوابق
الفكرة: وقت الحفظ، كل كلمة عربية تنحفظ في الفهرس بشكلها الموحّد، ومعها شكلها بدون السابقة. «للمذكرة» تنحفظ كـ«للمذكره» و«مذكره». ووقت البحث، كل كلمة تتحول لبحث ببداية الكلمة على الشكلين. فالفهرس يرجّع الجمل الأربع، وبسرعة FTS5.
الصعوبة الحقيقية إن العربي ملتبس: الواو في «وصل» والفاء في «فكرة» والباء في «بيت» من أصل الكلمة، مو سوابق. لازم قواعد تحدد متى تفصل: مثلًا تخلي الجذع حرفين على الأقل بعد «ال»، وثلاثة بعد حرف مفرد مثل و وب. وحتى مع هالقواعد يبقى التباس قليل: البحث عن «كرة» ممكن يلقى «فكرة».
وفيه حدود لازم تعرفها: فصل السوابق ما يحلّل اللواحق (مذكرتي، مذكرات)، وما يستخرج الجذور («كتب» ما تلقى «مكتبة»)، وما يعرف مرادفات اللهجات («وش» ما تلقى «ماذا»).
جرّبها بنفسك
في صفحة «بحث عربي ذكي» تجربة مباشرة: تكتب أي كلمة عربية، وتشوف نفس الـ20 جملة في جدولين FTS5، العادي والمعدّل، جنب بعض.
«بحث عربي ذكي» مكتبة جاهزة تسوي الحل الثاني كامل: توحيد، وفصل سوابق بقواعد مجرّبة، وفهرس FTS5 واستعلاماته، ومقتطفات تلوّن الكلمة. بـSwift وPython وJavaScript بنفس النتيجة، بـ29$ مرة وحدة.