بحثنا كان أعمى للمعنى: كيف أنقذتنا ‘قواعد بيانات المتجهات’ من جحيم البحث الحرفي؟

السلام عليكم يا جماعة الخير، معكم أخوكم أبو عمر.

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

بتذكر مرة، اتصل فيي مدير الجمعية معصّب، وبحكيلي: “يا أبو عمر، في زبونة من أمريكا بدها تشتري شال عليه تطريز فلاحي، كتبت في البحث ‘شال بتطريز فلاحي’ وما طلعلها ولا إشي! مع إنه عنا قسم كامل للأثواب والشالات المطرزة!”. نزلت راسي وأنا عارف المشكلة. نظام البحث اللي بنيناه كان “حرفي”، يعني بدور عالكلمة زي ما هي. هو ما بفهم إنه “تطريز فلاحي” هو نفسه “شغل إبرة تراثي” أو قريب من معنى “قطبة غزة” أو “ثوب مطرز”. بالنسبة إله، إذا الكلمة مش مكتوبة بالحرف، يبقى المنتج مش موجود. كان بحثنا أعمى، لا يرى المعنى، بل يرى الحروف فقط.

هذا الموقف كان الشرارة اللي خلتني أغوص في عالم جديد وقتها، عالم البحث الدلالي، واللي قادني مباشرةً للبطل الحقيقي لقصتنا اليوم: قواعد بيانات المتجهات (Vector Databases). تعالوا معي أحكيلكم كيف هالتكنولوجيا أنقذتنا من جحيم البحث الحرفي، وكيف ممكن تنقذ مشاريعكم أنتم كمان.

جحيم البحث الحرفي: ليش بحثنا كان “أعمى”؟

قبل ما نحكي عن الحل، لازم نفهم أصل المشكلة. معظمنا لما يبدأ بمشروع فيه بحث، أول إشي بيخطر بباله هو استخدام استعلام LIKE في قواعد بيانات SQL. الموضوع بسيط ومباشر:

SELECT * FROM products WHERE description LIKE '%تطريز فلاحي%';

هذا البحث، مع إنه سهل، إلا إنه محدود جداً:

  • حرفي للغاية: لا يفهم المرادفات (مثلاً: “سيارة” و “مركبة” كلمتان مختلفتان تماماً بالنسبة له).
  • لا يفهم السياق: جملة “أفضل هاتف بدون كاميرا” قد تُظهر لك نتائج عن “أفضل هاتف بكاميرا ممتازة” فقط لوجود الكلمات المشتركة.
  • حساس للأخطاء الإملائية: لو كتب المستخدم “تطريز فلاحي” بهمزة قطع بدل الوصل، قد لا يجد شيئاً.
  • محدود بلغة واحدة: لا يمكنه فهم أن “Handmade embroidery” هي ترجمة “تطريز يدوي”.

حتى محركات البحث المتقدمة المدمجة في قواعد البيانات (Full-Text Search) بتحسن الموضوع شوي من خلال التعامل مع جذور الكلمات (Stemming) وكلمات التوقف (Stop words)، لكنها بتضل في جوهرها تبحث عن الكلمات نفسها، مش عن المعنى الكامن وراها. كنا محتاجين قفزة نوعية، قفزة نحو الفهم الحقيقي.

شروق شمس الفهم: ما هي الـ Embeddings؟

هنا دخل الذكاء الاصطناعي على الخط. الحل يبدأ بمفهوم عبقري اسمه “التضمين” أو “المتجهات” (Embeddings). فكر فيها هيك: ماذا لو استطعنا تحويل أي قطعة محتوى (كلمة، جملة، صورة، مقطع صوتي) إلى مجموعة من الأرقام في فضاء متعدد الأبعاد؟

هذه المجموعة من الأرقام، اللي بنسميها “متجه” (Vector)، مش مجرد أرقام عشوائية. هي تمثيل رياضي للمعنى الدلالي للمحتوى. الأشياء اللي معانيها متشابهة، بتكون متجهاتهم “قريبة” من بعض في هذا الفضاء الرياضي. والأشياء اللي معانيها مختلفة، بتكون متجهاتهم “بعيدة”.

صار بإمكاننا نعمل عمليات حسابية على المعاني! أشهر مثال هو المعادلة الكلاسيكية:

متجه(“ملك”) – متجه(“رجل”) + متجه(“امرأة”) ≈ متجه(“ملكة”)

هذا يعني أن النموذج اللغوي فهم العلاقة بين “ملك” و “رجل” (علاقة جنس)، وطبقها على “امرأة” ليصل إلى “ملكة”. هذا هو الفهم الحقيقي!

من الكلمة إلى الرقم: كيف بتصير العملية؟

هاي العملية بتصير باستخدام نماذج لغوية ضخمة (LLMs) مدربة مسبقاً، مثل نماذج BERT أو نماذج OpenAI أو غيرها. هاي النماذج قرأت مليارات النصوص من الإنترنت وتعلمت العلاقات الدقيقة بين الكلمات والجمل. اليوم، استخدامها صار أسهل من شربة المي. شوفوا هالمثال البسيط باستخدام مكتبة sentence-transformers في بايثون:


from sentence_transformers import SentenceTransformer

# تحميل نموذج مدرب مسبقاً، بيدعم اللغة العربية
model = SentenceTransformer('paraphrase-multilingual-MiniLM-L12-v2')

sentences = [
    "شال عليه تطريز فلاحي",
    "ثوب فلسطيني مطرز يدوياً",
    "كوفية أصلية من الخليل",
    "سيارة رياضية سريعة"
]

# تحويل الجمل إلى متجهات (Embeddings)
embeddings = model.encode(sentences)

# كل جملة تحولت الآن إلى متجه (مصفوفة أرقام)
for sentence, embedding in zip(sentences, embeddings):
    print("الجملة:", sentence)
    # رح نطبع أول 5 أرقام من المتجه كمثال
    print("بداية المتجه:", embedding[:5])
    print("-" * 20)

الناتج رح يكون عبارة عن مصفوفة من الأرقام لكل جملة. الآن، الجملة الأولى والثانية، معانيهم متقاربة جداً، وبالتالي المتجهات الناتجة عنهم رح تكون رياضياً “قريبة” من بعضها، بينما متجه “سيارة رياضية” رح يكون بعيد جداً عنهم.

حلو كتير! صار عنا طريقة لتمثيل المعنى رقمياً. بس هلقيت بتيجي المشكلة الثانية: لو عندي ملايين المنتجات، يعني ملايين المتجهات، كيف بدي أبحث فيهم بكفاءة عن “أقرب” متجه لاستعلام المستخدم؟ البحث فيهم واحد واحد (Brute-force) عملية بطيئة ومستحيلة عملياً.

ولادة البطل: قواعد بيانات المتجهات (Vector Databases)

وهنا يأتي دور “قواعد بيانات المتجهات”. هي نوع جديد ومختص من قواعد البيانات، مصمم خصيصاً لتخزين والبحث في كميات هائلة من المتجهات (Embeddings) بسرعة فائقة.

على عكس قواعد البيانات التقليدية اللي بتفهرس البيانات باستخدام أشجار B-Trees للبحث السريع عن قيم محددة (مثل ID=5)، قواعد بيانات المتجهات بتستخدم خوارزميات فهرسة مختلفة تماماً، هدفها الأساسي هو إيجاد “الجيران الأقرب” (Nearest Neighbors) لمتجه معين.

السحر وراء الكواليس: البحث عن أقرب جار (ANN)

معظم قواعد بيانات المتجهات ما بتعطيك الجواب “الدقيق” 100%، بل بتستخدم خوارزميات “البحث التقريبي عن أقرب جار” (Approximate Nearest Neighbor – ANN). الفكرة هي التضحية بجزء بسيط جداً من الدقة مقابل سرعة هائلة في البحث.

تخيل إنك بدك تلاقي أقرب مطعم بيتزا لمكانك في مدينة ضخمة. الطريقة الدقيقة (Brute-force) هي إنك تقيس المسافة بينك وبين كل مطعم في المدينة، وهذا رح ياخذ وقت طويل. الطريقة التقريبية (ANN) هي إنك تحدد الحي اللي أنت فيه، وتبحث فقط في مطاعم هذا الحي والأحياء المجاورة. النتيجة رح تكون سريعة جداً، وبنسبة 99.9% رح تلاقي أفضل خيار أو خيار قريب جداً منه.

من أشهر خوارزميات ANN المستخدمة هي HNSW (Hierarchical Navigable Small World) و IVF (Inverted File Index)، وهي تقنيات ذكية بتنظم المتجهات بطريقة هرمية أو عنقودية بتخلي عملية البحث سريعة بشكل لا يصدق.

يلا نطبّق يا جماعة: مثال عملي من الألف للياء

الحكي النظري حلو، بس التطبيق أحلى. خلينا نرجع لمشكلة متجر التطريز ونحلها باستخدام قاعدة بيانات متجهات بسيطة اسمها ChromaDB، واللي ممكن تشغلها على جهازك مباشرة.

الخطوة الأولى: تجهيز البيانات وتحويلها لمتجهات

أولاً، نحتاج لمكتبة chromadb و sentence-transformers. ممكن تثبيتهم بسهولة:

pip install chromadb sentence-transformers

الآن، لنجهز بياناتنا ونحولها لمتجهات:


import chromadb
from sentence_transformers import SentenceTransformer

# 1. تحميل نموذج تحويل النصوص لمتجهات
model = SentenceTransformer('paraphrase-multilingual-MiniLM-L12-v2')

# 2. بيانات المنتجات (وصف بسيط لكل منتج)
products_data = [
    {"id": "prod1", "description": "ثوب فلسطيني تقليدي مطرز يدوياً بخيوط حمراء."},
    {"id": "prod2", "description": "شال من الكشمير الناعم، شغل يدوي متقن."},
    {"id": "prod3", "description": "قطعة ديكور خشبية من خشب الزيتون من بيت لحم."},
    {"id": "prod4", "description": "حقيبة قماشية عصرية عليها تطريز فلاحي بسيط."},
    {"id": "prod5", "description": "كوفية فلسطينية أصلية باللونين الأبيض والأسود."}
]

# 3. استخراج الأوصاف والمعرّفات
descriptions = [item["description"] for item in products_data]
ids = [item["id"] for item in products_data]

# 4. تحويل الأوصاف إلى متجهات
embeddings = model.encode(descriptions)

الخطوة الثانية: تخزين المتجهات في قاعدة البيانات

الآن، سنقوم بإنشاء “مجموعة” (Collection) في ChromaDB وتخزين المتجهات مع بياناتها الوصفية (Metadata).


# 1. إعداد عميل ChromaDB (سيتم تخزين البيانات محلياً في مجلد)
client = chromadb.PersistentClient(path="./chroma_db")

# 2. إنشاء مجموعة جديدة أو الحصول عليها لو كانت موجودة
# يمكننا تمرير اسم النموذج مباشرة لـ Chroma ليقوم هو بعملية التحويل
# لكن للتوضيح، قمنا بها يدوياً في الخطوة السابقة
collection = client.get_or_create_collection(name="palestinian_products")

# 3. إضافة البيانات للمجموعة
collection.add(
    embeddings=embeddings, # المتجهات الرقمية
    documents=descriptions, # النصوص الأصلية
    ids=ids # معرّفات فريدة لكل عنصر
)

print("تمت إضافة المنتجات بنجاح إلى قاعدة البيانات المتجهية!")

الخطوة الثالثة: البحث الدلالي السحري

هنا يكمن السحر. لنبحث الآن عن “شال بتطريز فلاحي”، وهي الجملة التي فشل فيها نظامنا القديم.


# 1. استعلام البحث من المستخدم
query_text = "شال بتطريز فلاحي"

# 2. تحويل استعلام البحث إلى متجه بنفس النموذج
query_embedding = model.encode([query_text])

# 3. تنفيذ البحث في ChromaDB عن أقرب 3 منتجات
results = collection.query(
    query_embeddings=query_embedding,
    n_results=3
)

# 4. طباعة النتائج
print(f"nنتائج البحث عن: '{query_text}'n")
for i, doc in enumerate(results['documents'][0]):
    print(f"النتيجة {i+1}: {doc}")
    print(f"المعرّف: {results['ids'][0][i]}")
    print(f"درجة التشابه (المسافة): {results['distances'][0][i]:.4f}")
    print("-" * 20)

عند تشغيل هذا الكود، ستكون النتائج المتوقعة كالتالي (قد يختلف الترتيب قليلاً):

نتائج البحث عن: ‘شال بتطريز فلاحي’

النتيجة 1: حقيبة قماشية عصرية عليها تطريز فلاحي بسيط.
المعرّف: prod4
درجة التشابه (المسافة): 0.2345

——————–
النتيجة 2: ثوب فلسطيني تقليدي مطرز يدوياً بخيوط حمراء.
المعرّف: prod1
درجة التشابه (المسافة): 0.4589

——————–
النتيجة 3: شال من الكشمير الناعم، شغل يدوي متقن.
المعرّف: prod2
درجة التشابه (المسافة): 0.6123

——————–

لاحظوا الإبداع! مع أن كلمة “شال” موجودة فقط في المنتج الثاني، إلا أن النظام فهم أن “تطريز فلاحي” هي الأهم في سياق البحث، فوضع المنتج الرابع (الحقيبة) في المقدمة لأنه يحتوي على هذه العبارة بالضبط، ثم أتى بالمنتج الأول (الثوب المطرز) لأنه يفهم أن “مطرز يدوياً” قريب جداً في المعنى من “تطريز فلاحي”، ثم أتى بالشال لأنه يحتوي على كلمة “شال” و “شغل يدوي”. هذا هو البحث الذي يفهم النية، وليس فقط الكلمات.

نصائح من خبرة أبو عمر

بعد ما اشتغلنا على مشاريع كثيرة فيها هاي التقنية، جمعتلكم كم نصيحة من القلب:

  • نصيحة 1: اختيار نموذج الـ Embedding المناسب. مش كل النماذج زي بعض. في نماذج متخصصة بالنصوص القصيرة (للبحث)، ونماذج للنصوص الطويلة (لتلخيص المستندات)، ونماذج متعددة اللغات. استثمر وقتك في تجربة عدة نماذج لاختيار الأفضل لمشروعك.
  • نصيحة 2: الـ Metadata هي كنزك. لا تخزن المتجه والنص فقط. خزّن معه كل البيانات الوصفية المفيدة (السعر، الفئة، تاريخ الإضافة، إلخ). هذا يسمح لك بعمل فلترة مسبقة (Pre-filtering) قبل البحث في المتجهات (مثلاً: ابحث عن “ملابس مطرزة” *فقط* في فئة “النساء” و *بسعر أقل* من 100 دولار). هذا يزيد من دقة وسرعة البحث.
  • نصيحة 3: لا تهمل التقييم. كيف تعرف أن بحثك جيد؟ جهز مجموعة من استعلامات البحث مع النتائج المثالية المتوقعة، وقم بتشغيلها بشكل دوري لتقييم أداء نظامك. هل النتائج تتحسن أم تسوء مع التغييرات التي تجريها؟
  • نصيحة 4: ابدأ ببساطة. لا تقفز مباشرة إلى حلول سحابية معقدة ومكلفة. ابدأ بمكتبات بسيطة مثل ChromaDB أو FAISS على جهازك. افهم المبدأ، جرب، وعندما يكبر مشروعك وتحتاج لقدرات أكبر، وقتها انتقل لحلول مثل Pinecone, Weaviate, أو Qdrant.

الخلاصة: من البحث الأعمى إلى البصيرة الاصطناعية 💡

رحلتنا من البحث الحرفي المزعج إلى البحث الدلالي الذكي كانت نقلة نوعية. قواعد بيانات المتجهات، مدعومة بقوة نماذج الـ Embeddings، لم تحل مشكلة تقنية فقط، بل فتحت الباب أمام تجارب مستخدم أذكى وأكثر سلاسة. لم نعد نجبر المستخدم على التفكير مثل الآلة، بل جعلنا الآلة تحاول أن تفهم ما يريده الإنسان.

هذه التقنية لم تعد حكراً على عمالقة التكنولوجيا. بفضل المكتبات مفتوحة المصدر والخدمات السحابية الميسّرة، أصبح بإمكان أي مطور، مثلي ومثلك، أن يضيف هذه القدرة “السحرية” إلى تطبيقاته. نصيحتي الأخيرة لك: لا تخف من التجربة. خذ مشروعاً صغيراً، مجموعة بيانات بسيطة، وابدأ اللعب بالمتجهات. ستتفاجأ من الإمكانيات التي ستفتحها أمامك. بالتوفيق يا أصدقائي! 🙏

أبو عمر

سجل دخولك لعمل نقاش تفاعلي

كافة المحادثات خاصة ولا يتم عرضها على الموقع نهائياً

آراء من النقاشات

لا توجد آراء منشورة بعد. كن أول من يشارك رأيه!

آخر المدونات

​معمارية البرمجيات

المونوليث كان وحشًا لا يمكن المساس به: كيف أنقذنا ‘نمط الخانق’ من جحيم التجميد التطويري؟

في هذه المقالة، أشارككم قصة حقيقية من قلب المعركة مع نظام موروث "مونوليث" كاد أن يشلّ فريقنا بالكامل. سأشرح لكم بالتفصيل نمط "الخانق" (Strangler Fig...

18 أبريل، 2026 قراءة المزيد
تجربة المستخدم والابداع البصري

واجهاتنا كانت حصونًا منيعة: كيف أنقذتنا ‘معايير الوصول الرقمي’ (WCAG) من جحيم استبعاد المستخدمين؟

أنا أبو عمر، مطور برمجيات فلسطيني. في هذه المقالة، أشارككم قصة غيرت نظرتي للبرمجة، وكيف حولت "معايير الوصول الرقمي" (WCAG) تطبيقاتنا من قلاع مغلقة إلى...

18 أبريل، 2026 قراءة المزيد
برمجة وقواعد بيانات

كودنا كان غارقًا في استعلامات SQL النصية: كيف أنقذتنا ‘مخططات الكائنات العلائقية’ (ORM) من جحيم الصيانة؟

أشارككم قصة من قلب المعركة البرمجية، كيف انتقلنا من فوضى استعلامات SQL المكتوبة يدويًا إلى عالم منظم وآمن باستخدام تقنيات ORM. هذه ليست مجرد مقالة...

18 أبريل، 2026 قراءة المزيد
الشبكات والـ APIs

خدماتنا كانت مكشوفة وفوضوية: كيف أنقذتنا ‘بوابة الواجهات البرمجية’ (API Gateway) من جحيم الإدارة اليدوية والأمان المهترئ؟

أشارككم قصة حقيقية من قلب المعركة البرمجية، كيف انتقلنا من فوضى الخدمات المصغرة والأمان المهترئ إلى نظام مركزي وآمن. اكتشفوا معنا كيف كانت بوابة الواجهات...

18 أبريل، 2026 قراءة المزيد
التوظيف وبناء الهوية التقنية

مقابلات تصميم النظم كانت صندوقًا أسود: كيف أنقذني ‘إطار عمل منهجي’ من جحيم الإجابات العشوائية؟

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

18 أبريل، 2026 قراءة المزيد
التوسع والأداء العالي والأحمال

طلباتنا كانت تضرب قاعدة البيانات بلا رحمة: كيف أنقذنا ‘التخزين المؤقت’ (Caching) من جحيم الاستجابة البطيئة؟

في هذه المقالة، أشارككم قصة حقيقية عن كيفية تحول تطبيقنا من بطيء ومكلف إلى صاروخي الأداء بفضل تقنية التخزين المؤقت (Caching). سنغوص في أعماق هذه...

18 أبريل، 2026 قراءة المزيد
البودكاست