تعلم كيف تتجاهل التشكيل والهمزات عند البحث بالنصوص العربية في قواعد بيانات Sql

أبو اليسر
بواسطه أبو اليسر في قسم الدروس والمواضيع المتميزة,
بسم الله الرحمن الرحيم الحمد لله والصلاة والسلام على رسول الله وعلى آله وصحبه أجمعين، أما بعد: في هذا الدرس سنتعلم بإذن الله كيف نقوم بتجاهل التشكيل والهمزات والتاء المربوطة وأحرف العلة من اللغة العربية عند البحث في النصوص العربية ضمن قواعد بيانات Sql. ملاحظات: • أكواد تجاهل الهمزات والتاء المربوطة وأحرف العلة تعمل في قواعد بيانات أكسس وقواعد بيانات Sql أما كود تجاهل التشكيل فلا يعمل إلا مع قواعد بيانات Sql • يوجد مثال للدرس مرفق مصمم في vb.net 2005 نظرة تاريخية: طرحت سؤالاً في منتديات الفريق العربي للبرمجة عن كيفية تجاهل التشكيل عند البحث باستخدام عبارات Sql وكان ذلك قبل ما يقارب السنتين.. ومن ذلك الوقت وأنا مستمر في البحث عن أفضل طريقة لتجاهل التشكيل عند البحث في النصوص العربية، حتى وصلت إلى أفضل حل ولله الحمد. هذا العناء كانت Microsoft تستطيع أن توفره على مبرمجي العرب لو قامت بتخديم اللغة العربية بشكل جيد في برامجها كما خدمت غيرها من اللغات.. فمثلاً: اللغات التي تحتوي على أحرف تشكيل غير اللغة العربية قامتMicrosoft بوضع خاصية لهم عند بناء قاعدة بيانات Sql وهي من خصائص الحقل لو وضعتها لقام معالج البحث بتجاهل التشكيل في هذه اللغات بكل تلقائي، هذه الخاصية هي: Collation = SQL_Latin1_General_CP437_CI_AI أو غيرها حسب اللغة.. فبهذا الخيار لو بحث المستخدم عن حرف a مثلاً ستظهر له كافة النتائج التي تحتوي على a والتي تحتوي على ä أو å. أما اللغة العربية فلا يوجد خيار لدعم تجاهل التشكيل لها في قواعد بيانات Sql.. ولهذا اضطر المبرمجون العرب لسلوك طرق مختلفة معوجة حتى يصلوا إلى طريقة لتجاهل التشكيل أثناء البحث في النصوص العربية وكانت هذه الطرق حسب ما رأيت كالتالي: طرق خاطئة: الطريقة الأولى: وهي أفضل طريقة من بين هذه الطرق لمن عنده نصوص كبيرة مشكلة، وهذه الطريقة تعتمد على فلترة النص من التشكيل قبل البحث فيه. وقد رأيت كوداً في منتديات Sql هنا يقوم بفلترة التشكيل من النص من خلال Function تضاف إلى قاعدة بيانات Sql وهو الطريق الأفضل من بين طرق الفلترة أثناء البحث.. ومن مشاكل هذه الطريقة أنها تقوم بتعبئة النص المفلتر في الذاكرة ثم تبحث فيه.. وأنها تبطأ عملية البحث بشكل كبير.. ولكن ماذا يفعل من كان عنده قاعدة بيانات نصوص حجمها يصل إلى حوالي 6 جيجا كما في برنامج المكتبة الشاملة الإصدار الثاني مثلاً، لا بد أن يسلك مثل ها الطريق لأنه الأفضل بالنسبة له من بين الطرق الأخرى هذه. الطريقة الثانية: وهي وضع حقل مفلتر من التشكيل للنصوص المشكلة بجانب الحقل الأصلي ثم البحث في هذا الحقل وعرض النتيجة من الحقل الأصلي. وهذه الطريقة هي المستخدمة في كثير من برامج البحث في نص القرآن الكريم لأن حجم قاعدة البيانات لنص القرآن الكريم صغيرة الحجم نسبياً.. ومن مشاكل هذه الطريقة أنها تزيد من حجم قاعدة البيانات وبالتالي لن تنفع مع من يبحث في نصوص كبيرة الحجم كما ذكرنا. الطريقة الثالثة: وهي الطريقة التي اعتمدتها شركة صخر في برامجها القديمة كبرنامج الحديث الشريف، حسب تصوري.. وهذه الطريقة تلزم المستخدم بعدم كتابة أي علامة من علامات التشكيل في كلمة البحث، ثم تلزمه باختيار التشكيل الذي يريده لكلمته من خلال قاموس مشكل للكلمات لديها، ثم تقوم بالبحث عن الكلمة التي كتبها بالتشكيل الذي اختاره.. ومشكلة هذه الطريقة أنها غير فعالة إلا مع شركة كشركة صخر لأن من يملك نصوصاً لم يكتبها هو كيف سيشكلها كلها؟ وكيف سيجد هذا القاموس الذي صنعته صخر، وكيف وكيف؟ إذن فهي طريقة لن تجدي نفعاً معنا.. علماً بأن شركة حرف غيرت ها الأسلوب في برامجها الجديدة كبرنامج جامع الفقه الإسلامي، ولكن عملية البحث في هذا البرنامج بطيئة نسبياً فلا أعرف ما هي الطريقة التي استخدموها ولعلهم وصلوا إلى حل صحيح مع قواعد البيانات التي يستخدمونها، أو أنهم يستخدمون الطريقة الأولى.. الطريقة الرابعة: وهي عن طريق استخدام ملفات وورد ثم البحث فيها من خلال برنامجك عن طريق استخدام ميزات Word والبحث عن طريقه من برنامجك وهذه طريقة فاشلة وغير مجدية.. وقد استخدمت هذه الطريقة في بعض برامج البحث في القرآن الكريم.. الطريقة الخامسة: عمد بعض مبرمجي العرب إلى فلترة النصوص من التشكيل بل ومن أحرف الهمزة واستبدالها من بأحرف من غير همزة فيقوم باستبدال أحمد بـ: احمد، واستبدال هدي بـ: هدى، واستبدال أسامة بـ: اسامه. ويا للعار، فهذه طريقة جداً خاطئة في نظري، فنحن نسعى لإيجاد طرق لتصحيح النصوص التي تكتب على الكمبيوتر وهذه الطريقة تسعى لتدمير كافة النصوص العربية التي ستكتب عليه.. بداية الطريق: كانت بداية النهاية لهذه الأزمة عند ما بدأت بتجربة استخدام قوسي [] ضمن عبارات Sql أثناء عملية البحث.. هذين القوسين [] لو استخدما في كلمة البحث من جملة Sql فسيقوم البرنامج بالبحث عن أحد الأحرف الموجودة ضمن القوسين بشرط وجود جميع الأحرف. فمثلاً: Select * from tbl where fld like '[أكل]' سيظهر البحث جميع النتاج التي تحتوي على أ ك ل بشرط وجود هذه الأحرف جميعها، فتظهر النتاج كالتالي: أكل يأكل مأكولات يأكلون وهذا يعني تقريباً أنه بحث عن جذر الكلمة، ويمكن اعتماد هذه الطريقة للبحث على مستوى الجذر رغم أنها ستظهر بعض النتائج الخاطئة.. والآن الخطوة الثانية: لو وضعنا هذين القوسين على بعض الحروف مثلاً: Select * from tbl where fld like '[أا]حمد' سيظهر جميع النتائج التي تحتوي على "أحمد" والتي تحتوي على "احمد"، لأننا وضعنا "أ" و"ا" ضمن القوسين فأصبحت اختيارية بالنسبة له، ولكن بشرط وجود أحد الأحرف التي بين القوسين. فلن يظهر النتائج التي تحتوي على "حمد" فقط. وهكذا نكون قد وصلنا إلى حل لتجاهل الهمزات أثناء البحث. أما تجاهل التاء المربوطة فمثل هذا: Select * from tbl where fld like 'بقر[ةه]' وبهذا ستظهر النتائج التي تحتوي على كلمة: "بقرة" والتي تحتوي على كلمة "بقره". ومثل هذا تجاهل أحرف العلة: Select * from tbl where fld like 'هد[اىيو]' عند البحث عن "هدا" أو "هدى" أو "هدي" ستظهر النتائج التي تحتوي على: هدا هدى هدي هدو ملاحظة: يمكن استخدام كافة خيارات Sql في هذه الحالة فيمكن وضع علامة % قبل وبعد الكلمة بشكل طبيعي. والآن كيف نستفيد من هذا الكود لتجاهل التشكيل: لو وضعنا علامات التشكيل كلها بين القوسين [] بعد كل حرف من أحرف الكلمة ماذا سيحدث؟ سيعطينا البرنامج كافة النتائج التي تحتوي على تلك الكلمة بشرط أن تكون مشكلة أي بعد كل حرف لا بد أن توجد إحدى علامات التشكيل الموضوعة بعده بين القوسين. Select * from tbl where fld like 'م[ِ َ]ن' مثلاً: الكود السابق سيعطينا النتائج التي تحتوي على "مَن" المشكلة بالفتح بعد الميم. و"مِن" المشكلة بالكسر بعد الميم. لكنه لن يعطينا النتائج التي لا تحتوي بعد الميم لا على علامة الفتح ولا على علامة الكسر يعني لن يعطينا "من" الغير مشكلة. لماذا؟ كما ذكرنا أن الحروف التي ما بين القوسين مطلوب أحدها، فيرجع كافة القيم التي تحتوي على أحد الأحرف الموجودة بين القوسين ولا يرجع القيم التي ترجع 0 صفر حرف من الأحرف الموجودة بين القوسين. هذه أول مشكلة، والمشكلة الأخرى: هي أنه لا يأخذ أكثر من حرف معاً من بين الأحرف التي بين القوسين فمثلاً لو كتبنا: Select * from tbl where fld like '[أا]حمد' سيرجع النتائج: أحمد احمد ولن يرجع النتيجة: أاحمد ولا النتيجة: أأحمد وبالتالي فعند استخدامه للتشكيل لن تكون النتيجة صحيحة لأن علامة الشدة قد يكون فوقها علامة الفتح أو الكسر... فلو وضعنا علامة الشدة وعلامة الفتح وعلامة الكسر بين القوسين فسيرجع القيم التي تحتوي على: علامة الشدة وحدها؛ علامة الفتحة وحدها؛ علامة الكسرة وحدها؛ ولن يرجع النتيجة التي تحتوي على علامة الشدة مع الفتحة مثلاً. الحل: أصل هذين القوسين [] من العبارات الاعتيادية Regular Expressions ولكن عند استخدامهما مع جمل Sql بشكل مباشر يقوم بتنفيذ ما ذكرنا ولا يقوم بتنفيذ كافة خيارات العبارات الاعتيادية ضمن عبارات Sql. لماذا نحتاج لاستخدام خيارات العبارات الاعتيادية هنا ضمن عبارات Sql؟ لأننا لو استطعنا استخدامها نكون قد وصلنا إلى حل للمشكلتين التين ظهرتا معنا عند استخدام القوسين [] لتجاهل التشكيل. المشكلة الأولى أنها لا ترجع القيمة صفر من القيم التي بين القوسين، والمشكلة الثانية أنها لا تأخذ أكثر من حرف معاً من الأحرف الموجودة بين القوسين. وحل هاتين المشكلتين عند استخدام العبارات الاعتيادية هي فقط وضع علامة * بعد هذين القوسين. فمثلاً: 'م[ِ َ ّ]*ن' هذه العبارة سترجع عند استخدام العبارات الاعتيادية القيم التالية: "من" التي فوق الميم فتحة أو فوق الميم كسرة أو فوق الميم شدة. "من" التي من غير تشكيل. "مَّن" التي فوق الميم شدة وفوقها فتحة أو كسرة. وهذا هو المطلوب تماماً.. فكيف نستخدم العبارات الاعتيادية ضمن عبارات البحث في Sql؟ لمعرفة التفاصيل: اقرأ هذا الموضوع: http://www.u2u.be/Article.aspx?ART=RegExp ولعدم معرفة التفاصيل، اتبع ما يلي: قم بتحميل المرفق الثاني مع هذا الدرس والمسمى: "SqlRegExDB" وبعد تشغيله قم بفتح نافذة My Project واذهب إلى صفحة Database ثم قم بتحديد قاعدة بيانات Sql مشروعك، عن طريق الزر استعراض. ستظهر لك نافذة الاتصال بقاعدة البيانات وسيطلب منك أولاً تحديد اسم السيرفر: اكتب: "localhost\SQLEXPRESS".. انظر الصورة: بعد تحديد القاعدة والموافقة ستظهر لك رسالة لتفعيل CLR على قاعدة البيانات اختر موافق. ملاحظة: سنحتاج لتفعيل CLR على القاعدة قبل كل اتصال بها وهذا لتستطيع القاعدة أن تفهم ال function المكتوب بال vb.net والمضاف إليها.. سنتعلم فيما بعد إن شاء الله الطريقة السهلة لتفعيل CLR. الآن أغلق نافذة My Project ثم انقر بالزر الأيمن للفأرة فوق اسم البرنامج في solution explorer ثم اضغط الأمر Rebuild ثم الأمر Deploy بعد انتهاء تنفيذ الأمرين تكون قاعدة بياناتك جاهزة وقد أضيف إليها Function لاستخدام العبارات الاعتيادية عند التعامل مع هذه القاعدة، واسم هذا الFunction هو: ContainsRegExp الآن أغلق هذا المشروع ولن تحتاج إليه إلا عند صنع قاعدة بيانات جديدة. بعد ذلك قم باستخدام قاعدة بياناتك في أي مشروع vb.net أو غيره ولكن عندما تريد الاتصال بها يجب أن تقوم بتفعيل CLR قبل الاتصال بها هذا إذا كنت تريد استخدام الFunction السابق الذي وضعته فيها. لتفعيل CLR استخدم هذا الFunction : Friend Function RUNCLR(ByVal MyLibName As String) As Boolean
Try
Dim ClrCom As New SqlClient.SqlCommand
ClrCom.CommandText = "sp_configure 'clr enabled', 1; RECONFIGURE"
Dim Cn As New SqlClient.SqlConnection
Cn.ConnectionString = "Data Source=.\SQLEXPRESS;AttachDbFilename=" & MyLibName & ";Integrated Security=True;User Instance=True"
Cn.Open()
ClrCom.Connection = Cn
ClrCom.ExecuteNonQuery()
Cn.Close()
Return True
Catch ex As Exception
Return False
End Try
End Function استدعه وضع في MyLibName اسم قاعدة بياناتك. الآن اتصل بها بشكل عادي، وبذلك أصبحت القاعدة جاهزة لاستخدام العبارات الاعتيادية ضمن عبارات Sql بشكل طبيعي وبالتالي نستطيع أن نستخدم العبارة المطلوبة لتجاهل التشكيل. وسيكون شكل CommandText عند البحث مع تجاهل التشكيل على النحو التالي: "Select * from tbl WHERE (dbo.ContainsRegExp(fld, N'" & TxtSearch & "') = 1)" انتبه لمكان وضع اسم الحقل الذي سيتم البحث فيه fld وأما TxtSearch فهو نص البحث مضاف إليه الشكل الذي اتفقنا عليه قبل قليل لتتم عملية تجاهل التشكيل، وللتذكرة هذا هو: 'م[ِ َ ّ]*ن' الآن وإتماماً للفائدة قمت بكتابة Function تقوم بإعداد كلمة البحث على الشكل المطلوب لكل من تجاهل التشكيل وتجاهل الهمزات وتجاهل التاء المربوطة وتجاهل أحرف العلة: Friend Function PrepareWords(ByVal MyWords As String, ByVal IgnoreTashkeel As Boolean, ByVal IgnoreHamzat As Boolean, ByVal IgnoreTahMarbota As Boolean, ByVal AhrofAlela As Boolean)
Dim MyNewString As String = ""
If IgnoreTashkeel Then
Dim IgnrTshForm As String = "[ًٌٍَُِّْ~`]*"
For i As Integer = 0 To Len(MyWords) - 1
MyNewString += Mid(MyWords, i + 1, 1) + IgnrTshForm
Next
Else
MyNewString = MyWords
End If
Dim uu As Integer = Len(MyNewString)
Dim aaa(uu - 1) As String
For i As Integer = 0 To uu - 1
aaa(i) = Mid(MyNewString, i + 1, 1)
Next
For t As Integer = 0 To aaa.Length - 1
Select Case aaa(t)
Case "ا"
If IgnoreHamzat Then aaa(t) = "[اأإآؤئء]"
If AhrofAlela Then aaa(t) = "[اويى]"
Case "أ"
If IgnoreHamzat Then aaa(t) = "[اأإآؤئء]"
Case "إ"
If IgnoreHamzat Then aaa(t) = "[اأإآؤئء]"
Case "آ"
If IgnoreHamzat Then aaa(t) = "[اأإآؤئء]"
Case "ؤ"
If IgnoreHamzat Then aaa(t) = "[وأإآؤئء]"
Case "ئ"
If IgnoreHamzat Then aaa(t) = "[أىإآؤئءي]"
Case "و"
If IgnoreHamzat Then aaa(t) = "[وأإآؤئء]"
If AhrofAlela Then aaa(t) = "[اويى]"
Case "ي"
If IgnoreHamzat Then aaa(t) = "[أإآؤئءي]"
If AhrofAlela Then aaa(t) = "[اويى]"
Case "ء"
If IgnoreHamzat Then aaa(t) = "[أإآؤئء]"
Case "ى"
If IgnoreHamzat Then aaa(t) = "[أىإآؤئء]"
If AhrofAlela Then aaa(t) = "[اويى]"
Case "ة"
If IgnoreTahMarbota Then aaa(t) = "[ةه]"
Case "ه"
If IgnoreTahMarbota Then aaa(t) = "[ةه]"
End Select
Next
MyNewString = Join(aaa, "")
Return MyNewString
End Function ما عليك سوى استدعاء هذه الFunction وإعطائها كلمة البحث مع تحديد الخيارات المطلوبة لترجع لك الكلمة بالشكل المطلوب، قم باستدعائها على النحو التالي: Dim TxtSearch As String = PrepareWords(TextBox1.Text, True, True, True, True) الخيار الأول لتجاهل التشكيل الخيار الثاني لتجاهل الهمزات الخيار الثالث لتجاهل التاء المربوطة الخيار الرابع لتجاهل أحرف العلة والآن قم بالبحث عن TxtSearch بعد تحضيرها في ذلك الFunction كما يلي: Dim MyWhere As String = "Select * from book WHERE (dbo.ContainsRegExp(MyTxt, N'" & TxtSearch & "') = 1)" وهكذا سيقوم البرنامج بتنفيذ عملية البحث مع كافة الخيارات المطلوبة.. وستدعو لي بالتوفيق إن شاء الله. ملاحظة: في الكود السابق لا يمكنك استخدام علامة % قبل وبعد نص البحث لأننا أبحنا نتحدث عن أكواد العبارات الاعتيادية وهو بشكل عادي في الكود السابق سيعطيك النتيجة تماماً كما لو وضعت علامة % قبل وبعد نص البحث في عبارة Sql. ولكن حتى تقوم بعملية بحث مطابق يعني كما لو حذفت علامة % من قبل النص ومن بعده في عبارة Sql قم بوضع علامة ^ قبل نص البحث، وعلامة $ بعد نص البحث، فيصبح الكود على الشكل التالي: "Select * from book WHERE (dbo.ContainsRegExp(MyTxt, N'^" & TxtSearch & "$') = 1)" بهذا الكود سيقوم البرنامج بإجراء عملية البحث بشل مطابق مع تجاهل التشكيل وغيره كما تحدده في نص البحث. الخاتمة: هذا الدرس آمل أن يكون حلاً لمأساة طالما عانى منها كثيرون بسبب تجاهل Microsoft لتخديم اللغة العربية بشكل جيد في برامجها.. وأنا أتصور وبما أننا وصلنا إلى الحل أن تخدم Microsoft خيارات اللغة العربية في الإصدارات القادمة من برامجها، كما هي عادتها دائماً! وفي النهاية أقول: رغم أنه يوجد الكثير من النقاط في هذا الشرح تركتها شبه مجهولة من غير تفصيل وذلك لضيق الوقت إلا أنني آمل أن يفيد هذا الكود مبرمجي المسلمين.. وأنا سأعمل بإذن الله لتطوير برنامج ليكون بمثابة محرك بحث باللغة العربية يدعم كل خياراتها، وأسأل الله التوفيق؛ وأتصور أن تكون الخيارات التي في هذا المحرك كالتالي: يوجد ملفين مرفقين.. هذا وآخر دعوانا أن الحمد لله رب العالمين. حسبي الله ونعم الوكيل.. لم أعرف كيف أرفق الملفات، مع أنني أرفقت ملفات من قبل.. ولكن لا أجد أي زر أمر لإرفاق ملفات!! <_<
  • 43 ردود