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

الفكرة في جملة واحدة
بدل أن يكون عندي كود بحث معقّد داخل open-mushaf-native،
أصبحت أستدعي دالة واحدة من الحزمة:
const response = search(query, quranData, morphologyMap, wordMap, options, {
page,
limit,
});
والتطبيق يعرض response.results مع تظليل وترقيم صفحات، ولا يعرف أي تفاصيل أخرى عن منطق البحث.
الخطوة 1: توصيل الحزمة بالتطبيق
في هوك البحث في التطبيق أضفت:
import {
search,
type QuranText,
type MorphologyAya,
type WordMap,
} from 'quran-search-engine';
واستخدمت نفس البيانات الموجودة عندي في المشروع:
quranData: نص القرآن
morphologyMap: بيانات الصرف بصيغة Map
wordMap: خريطة الكلمات للجذر والصيغة
لا أحتاج لتغيير طريقة تخزين البيانات، فقط أمرّرها كما هي للحزمة.
الخطوة 2: هوك صغير يلف الحزمة
كل منطق البحث الآن داخل هوك واحد في التطبيق:
const { pageResults, counts, getPositiveTokens } = useQuranSearch({
quranData,
morphologyData: MORPH,
wordMap: WORD_MAP,
query,
advancedOptions,
page,
limit: PAGE_SIZE,
});
ما يهمني في التطبيق فقط:
pageResults: نتائج الصفحة الحالية
counts: إحصائيات البحث (نصي، جذر، صيغة، تقريبي)
لا أتعامل مع كيف تم البحث، هذا داخل quran-search-engine.
الخطوة 3: الترقيم (pagination) وواجهة البحث + سهولة الـ infinite scroll
في شاشة البحث:
- أحتفظ بـ
page و results
- عند تغيير
pageResults من الهوك:
- لو
page === 1 أستبدل results
- لو
page > 1 أضيف النتائج الجديدة على القديمة
useEffect(() => {
if (!query.trim()) {
setResults([]);
setHasMore(false);
setIsLoadingMore(false);
return;
}
if (!pageResults) return;
setResults((prev) => (page === 1 ? pageResults : [...prev, ...pageResults]));
const more = pageResults.length === PAGE_SIZE;
setHasMore(more);
setIsLoadingMore(false);
}, [pageResults, page, query]);
الـ FlatList هي التي تطلب الصفحة التالية:
<FlatList
data={results}
onEndReached={() => {
if (!hasMore || isLoadingMore) return;
setIsLoadingMore(true);
setPage((prev) => prev + 1);
}}
onEndReachedThreshold={0.5}
/>
الجميل هنا أن الحزمة نفسها تدعم الترقيم { page, limit }،
فأنا لم أكتب أي منطق معقّد للـ infinite scroll:
- فقط زدت رقم
page
- مرّرته إلى
search(...)
- الحزمة أرجعت صفحة جديدة من النتائج
بهذه البساطة تحوّل البحث إلى قائمة لا نهائية بسطرين كود تقريبًا في الشاشة.
الخطوة 4: التظليل (highlight) في الواجهة فقط
الحزمة ترجع لكل آية:
matchedTokens
tokenTypes (مثلاً: exact, lemma, root, fuzzy)
أنا في التطبيق أستخدم هذه المعلومات لألوّن النص:
- أستخرج الكلمات حسب النوع عبر
getPositiveTokens
- أرسلها إلى
HighlightText
- أختار الألوان في الواجهة
الحزمة لا تعرف شيئًا عن الألوان أو React Native،
تقول لي فقط “هذه الكلمات طابقت”، والباقي على الواجهة.
النتيجة: الحزمة = منطق البحث، التطبيق = واجهة فقط
بعد هذا التغيير:
quran-search-engine تحتوي على كل منطق البحث:
- تنظيف النص العربي
- البحث النصي
- البحث بالصِّيغة والجذر
- البحث التقريبي (fuzzy)
- الترقيم (pagination)
- معلومات التظليل (matchedTokens + tokenTypes)
open-mushaf-native:
- يمرّر البيانات للحزمة
- يحصل على نتائج جاهزة
- يهتم فقط بعرض النتائج، التظليل، الترقيم، والتنقّل
استبدال المنطق كان مباشرًا:
- استيراد الحزمة
- كتابة هوك بسيط يلف
search(...)
- تعديل شاشة البحث لتستخدم
pageResults وcounts والـ pagination الجاهز
الخلاصة: مزايا الحزمة وكيف يمكن إعادة استخدامها
ماذا قدّمت لي quran-search-engine عمليًا؟
- منطق بحث نظيف وجاهز للإنتاج
- واجهة بسيطة (
search(...) مع options وpagination)
- دعم مدمج لـ:
- تطبيع النص العربي (normalization)
- البحث النصي
- lemma + root
- fuzzy search
- highlight ranges عبر
matchedTokens وtokenTypes
- مكتوبة TypeScript، stateless، ولا تعتمد على إطار واجهة معيّن
هذا جعل دمجها في open-mushaf-native سهلًا جدًا:
أستدعي دالة واحدة، وأبني تجربة المستخدم حولها فقط.
كيف يمكن لمطورين آخرين الاستفادة؟
أي شخص عنده:
- بيانات قرآن (نص، صرف، word map)، أو حتى استعمال نسخة من البيانات الجاهزة من المكتبة
- مشروع React / React Native / Vue / Angular / Node / أي إطار آخر
يستطيع:
- تركيب الحزمة (
npm install quran-search-engine مثلاً)
- استدعاء
search(...) مع { page, limit }
- استخدام النتائج في واجهته الخاصة
- تصميم التظليل والألوان والطريقة التي يعرض بها النتائج كما يحب
بكلمات بسيطة:
- الحزمة تكون “محرك البحث القرآني” المشترك
- كل تطبيق يركّب فوقها واجهته الخاصة
بالنسبة لي، هذا بالضبط ما كنت أحتاجه في open-mushaf-native:
محرك بحث واحد واضح، يمكن إعادة استخدامه،
وتطبيق مصحف يركّز فقط على تجربة قراءة وبحث مريحة للمستخدم.