كيفية تسريع استعلامات MongoDB Regex بواسطة عامل يصل إلى 10

باستخدام قواعد بيانات NoSQL ، من السهل إنشاء مستندات تحتوي على مجموعة من العناصر. على سبيل المثال ، تخيل قاعدة بيانات للأفلام حيث يحتوي كل مستند على عنوان فيلم والممثل.

{
    العنوان: "ماتريكس" ،
    طاقم العمل: ['كيانو ريفز' ، 'كاري آن موس']
}

للاستعلام عن فيلم مع Carrie-Anne Moss ، سنقوم ببساطة بتشغيل db.movies.find ({cast: 'Carrie-Anne Moss'}) لاستعادة وثيقة المطابقة.

استخدام Regex لاستعلامات البحث غير الدقيقة

لسوء الحظ ، ليس هذا هو كيفية قيام المستخدمين بإدخال البيانات في حقل البحث.
يمكن أن يدخلوا شيئًا مثل "كاري موس" أو "موس كاري آن" وسيعثر استعلام البحث الدقيق () هنا.

توفر التعبيرات المعتادة (regex) طريقة لمطابقة الأوتار مع نمط ما ويأتي MongoDB مع محرك regex مدمج.

باستخدام regexes يمكن تنفيذ البحث يلقي مع استعلام مثل

db.movies.find ({
    cast: {$ elemMatch: {$ regex: / Moss / i، $ regex: / Carrie-Anne / i}}
})؛

سيعيد $ elemMatch تلك السجلات ، حيث يتطابق عنصر الصفيف مع كلا المعيارين - على النقيض من ذلك ، باستخدام $ عادي و (وهو الافتراضي لقائمة من المعايير) دون أن يعرض $ elemMatch أفلامًا مع "كاري آن موس" ولكن أيضًا تلك حيث يلعب كل من "ساندرا موس" و "كاري آن فيشر" دور البطولة معًا. سيكون هذا مجموعة كبيرة من المعلومات التي نريد استرجاعها.
لاحظ أيضًا "i" التي تجعل حساس الحالة غير قابل للتكرار. يتعين علينا إضافة ذلك ، لأنه لا يمكننا الاعتماد على المستخدمين لديك لاستخدام مفتاح التحول الخاص بهم كما ينبغي.

في الاختبارات الأولى الخاصة بك سوف يعمل هذا بشكل جيد ولكن بمجرد أن تنمو قاعدة البيانات وقاعدة المستخدم الخاصة بك ، ستكتشف أن استعلامات regex هذه

  1. تستهلك الكثير من الوقت وحدة المعالجة المركزية
  2. بطيئة للغاية

لماذا لا يمكننا فقط إضافة فهرس؟

الفهارس هي أول ما يجب مراعاته عند تحسين أداء الاستعلام باستخدام أي قاعدة بيانات. وثائق MongoDB واضحة إلى حد بعيد أننا محظوظون في هذه الحالة ، لأن regex غير حساس لحالة الأحرف. وحتى إذا كنا قد أنشأنا صفيفًا مع جهات فاعلة ذات درجات منخفضة ، فلم نتمكن بعد من الاستفادة من الاستعلامات المحسنة لأنه لا يمكننا استخدام ^ anchor لوضع علامة على بداية النص. لماذا ا؟ لأن "كاري آن موس" و "موس كاري آن". نحن ببساطة لا نعرف كيف تبدأ السلسلة التي نبحث عنها.

لذلك لا فهارس منتظمة بالنسبة لنا. لكن الإصدارات الأخيرة من MongoDB تدعم فهارس النص كذلك.
تتيح لك فهارس النص إجراء استعلامات البحث في سلاسل عشوائية. يجب أن يكون هذا بالضبط ما هو مطلوب لاستعلامنا المصبوب

فهارس النص الآمن لنا

حسنًا - ليس بهذه السهولة. فهارس النص في MongoDB تأتي مع بعض التحذيرات:

  • إذا كنت تريد فهرسة حقول متعددة في مستند ، فسيتم الاستعلام عنها جميعًا في استعلام بحث نصي. الوسائل: لا توجد طريقة لتحديد الحقول لمطابقتها. لذلك إذا قمت بإضافة قائمة من المخرجين لكل فيلم في وقت لاحق ووضع فهرس نص عليه ، فسيبحث البحث عن مخرجين وممثلين.
  • فهي افتراضيا واسعة جدا. إن البحث عن 'Sean Connery' سيمنحنا جميع الأفلام ، التي تتضمن بعض الممثلين الذين يدعى 'Sean' ، وجميع أنواع 'Connerie و جنبا إلى جنب مع' Sean Connery 'المحبوب لدينا.

من ناحية أخرى ، تكون استعلامات البحث عن النص سريعة وفعالة.
هل يمكننا استخدام المستندات المؤهلة مسبقًا لإجراء بحث دقيق؟

لذلك دعونا نبدأ بإضافة هذا الفهرس إلى مجموعتنا:

db.movies.createIndex ({cast: "text"})؛

بعد ذلك ، يمكننا تجربة أول استعلام بحث:

db.movies.find ({$ text: {$ search: "Moss Carrie-Anne"}})؛

كما ذكرنا ، سيعود ذلك بنتيجة ولكن أيضًا إيجابيات كاذبة أو حالة الاستخدام.

الجمع بين البحث عن نص مع مطابقة Regex

أنت تعلم أنه في عبارة شرطية ، مثل ififunc (somefunc () && someOtherFunc ()) {} ، لن يتم تقييم someOtherFunc () إذا أعاد someFunc () خطأ. وغالبًا ما يشار إليها باسم "الدائرة القصيرة". الأمر نفسه ينطبق على استعلامات MongoDB. هذا يعني أنه إذا استخدمنا وتوصلنا شرطين بشكل منطقي ، فلن يتم تنفيذ الشرط الثاني إذا لم يقم الأول بإرجاع أي بيانات.

بالإضافة إلى ذلك ، تكون قواعد البيانات ذكية بدرجة كافية لتقليل الاستعلام الثاني إلى مجموعة نتائج الأول ، لذا إذا أخذنا استعلام مثل {a: 1، b: 2} ، فسنجد أولاً جميع السجلات التي تحتوي على a: 1 ثم نخفض النتيجة إلى جميع السجلات مطابقة ب: 2 كذلك.

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

db.movies.find ({
$ و: [{
    النص $: {
        $ search: "Moss Carrie-Anne"
    }}، {
    طاقم العمل: {
        $ elemMatch: {$ regex: / Moss /، $ regex: / Carrie-Anne /}}
    }]}
)؛

دعني أكرر:

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

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

بالمناسبة - هذا ليس ملائمًا فقط لـ MongoDB أو حتى للاستعلامات النصية مقابل regex. في الواقع ، يمكن أن يؤدي اختيار ترتيب شروطك بحكمة إلى زيادة الأداء بشكل كبير مع أي قاعدة بيانات.

HTH :)