مكتبتي

شريط جانبي متعدد الجذور

flex-layout

  • Getting Started

  • Guides

  • Reference

typed-rx-http

  • Getting Started

  • Guides

  • Reference

global-rx-state

  • Getting Started

  • Guides

  • Reference

webflux-fe-dev-assistant

reactive-mongo-dsl

نظرة عامة

تاريخ ومبادئ تصميم مساعد استعلام صغير لمطور RDBMS الذي كان جديدًا على MongoDB، والذي نما وفقًا لمتطلبات المشروع الفعلي حتى أصبح طبقة راحة MongoDB الأولى في 1.0.0.


قصة الأصل

لقد قمت بتطوير RDBMS لمدة حوالي 4 سنوات، وكنت معتادًا على بيئات التشغيل في الغالب على AWS RDS. عندما بدأت مشروعًا شخصيًا جديدًا، اخترت MongoDB لأول مرة، وكلما تعرفت عليه، شعرت أنه أداة يمكن أن تغير إنتاجيتي في التطوير بشكل كبير بدلاً من كونه مجرد قاعدة بيانات ذات مخطط مرن.

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

كانت المشكلة أن كتابة استعلامات ديناميكية كانت بطيئة جدًا بالنسبة لي، كمستخدم جديد لـ MongoDB. في البداية، كان شكل findBy... في المستودع كافيًا، لكن مع تعقيد شروط البحث، كان علي تجميع Query و Criteria والتعبيرات الخاصة بالعمليات يدويًا. كان من السهل ارتكاب أخطاء مطبعية أثناء تجميع الشروط المتكررة في حالة عدم الاعتياد، كما أن كتابة نفس الشكل من التعليمات البرمجية باستمرار أبطأت سرعة التطوير.

بدأت في DSL بسيط للاستعلام

لذا، كان أول ما أنشأته هو فئة صغيرة لصنع R من CRUD ببساطة. هذه هي نقطة البداية للفئة التي تُعرف الآن باسم ReactiveMongoDsl. في ذلك الوقت، لم تكن هناك أي ميزات مثل التجميع، أو خطوط الأنابيب، أو البحث، وكانت مجرد وسيلة لتجميع Query و Criteria و paging بشكل أسرع قليلاً. كنت أعتقد أن بقية العمل يمكن القيام به باستخدام ReactiveMongoTemplate الأساسي أو MongoDB Driver مباشرة.

لكن رأي المطور الذي كنت أعمل معه كان مختلفًا. كان هو أيضًا جديدًا على MongoDB، وعندما كان يحتاج إلى عمليات مثل upsert أو bulk، كان يسأل أولاً عما إذا كانت تلك الميزات موجودة في DSL الذي أنشأته. كان بإمكاني أن أخبره باستخدام API الأساسي مباشرة، لكنني اعتقدت أنه سيواجه نفس الصعوبات التي واجهتها عندما تعلمت MongoDB لأول مرة. لذلك، بدأت في إضافة ميزات جديدة إلى ReactiveMongoDsl كلما ظهرت الحاجة.

في البداية، كان يحتوي على حوالي 600 سطر، وعندما وصل إلى 1,000 سطر، لم أعتقد أنه من الضروري تقسيمه إلى عدة فئات. عندما اقترب من 2,000 سطر، كنت أفكر قليلاً، لكن الهيكل الذي كان من المفترض أن يفصل كان يبدو أنه يكبر أكثر، والأهم من ذلك، كنت أشعر بالملل من تلك المهمة. عندما تجاوز 5,000 سطر، لم أستطع تأجيل الأمر أكثر، لذا قمت بنقل بعض الميزات القابلة للفصل إلى الخارج، لكنني كنت في حالة من الضغط الكبير لتقسيم التدفق الأساسي بدقة. السبب في أن الفئة الأساسية كبيرة ليس لأنها صممت DSL ضخم منذ البداية، بل لأنها تاريخ تراكم الميزات المطلوبة في مشروع فعلي عند نفس نقطة الدخول.

بعد إضافة الاستعلامات، والتجميع، والبحث، والتحديثات الذرية، والعمليات الضخمة، والتاريخ، و Atlas Search و Vector Search، وجدت أن معظم الميزات التي أحتاجها عند استخدام MongoDB في مشروع Java كانت موجودة. بدلاً من الاحتفاظ بها كمساعد داخلي في مشروع واحد، قمت بفصلها بحيث يمكن استخدامها بنفس الطريقة في مشاريع متعددة، وهذه هي عملية ولادة reactive-mongo-dsl.

من مساعد Spring إلى مكتبة تعتمد على Driver-first.

كانت نقطة البداية هي مساعد لتسهيل استخدام ReactiveMongoTemplate، لكن مع زيادة الميزات وبدء إعادة استخدامها في مشاريع متعددة، قررت أن نموذج التنفيذ الخاص بإطار عمل معين لا ينبغي أن يكون هو حدود المكتبة. يعتمد جوهر 1.0.0 على MongoExecutionContext كعقد تنفيذ ويستخدم MongoDB Reactive Streams Driver مباشرة.

هذه التغييرات ليست لاستبعاد Spring Data MongoDB. في تطبيقات Spring، يمكن ربط تسمية مجموعة ReactiveMongoTemplate و MongoConverter والتحويلات المخصصة والتدقيق التفاعلي بمحول MongoExecutionContext. يبقى الجوهر مستقلًا عن الإطار، وتستخدم التطبيقات الضرورية فقط إعدادات Spring الحالية.

في نفس الوقت، أصبح من الواضح أكثر أن الاتجاه هو عدم إعادة تنفيذ الميزات التي يوفرها MongoDB Driver بالفعل داخل DSL. يمكن أن تستقبل التجميعات العامة مرحلة Bson كما هي، وللبحث/المتجهات، هناك ممرات مثل SearchOperator و VectorSearchQuery و driverOptions(...) و stage(Bson). هذا خيار يهدف إلى تمكين المستخدمين من استخدام الميزات الجديدة التي يقدمها السائق دون انتظار الإصدار التالي من DSL.

فلسفة التطوير

لا أخفي MongoDB.

لم أكن أرغب في إنشاء ORM لتحويل ميزات MongoDB إلى مفاهيم قواعد بيانات أخرى. أهدف إلى ربط استعلامات MongoDB والتجميع والمعاملات و Atlas Search و Vector Search بتدفقات أقصر وأسهل في الاكتشاف.

أضيف فقط الميزات التي كانت ضرورية بالفعل.

هذه ليست مكتبة تم تنفيذها بعد تصميم قائمة الميزات أولاً. تم إضافة ميزات البحث و upsert و bulk لأن المشروع التشغيلي يحتاج إلى الاستعلام.

نفضل الإنتاجية على الكمال الهيكلي.

لا ندعي أن وجود فئة رئيسية كبيرة هو الهيكل المثالي. كان من الأهم في البداية أن يتعامل الفريق مع مهام MongoDB غير المألوفة بنفس الطريقة بسرعة، وما زلنا نفضل تدفق الاستخدام الفعلي على التجريد نفسه.

لا نعيد إنشاء ما يقوم به Driver بالفعل.

إذا كان هناك بناء مخصص في MongoDB Java Driver، فإننا نستخدم هذا النوع قدر الإمكان. يتم توفير التكرارات القابلة للتقليل من خلال API ملائم، لكننا لا نقوم بنسخ API Driver مرة أخرى مع تغيير الاسم.

يجب أن نكون قادرين على النزول إلى API الأساسية.

لا نعتقد أن DSL يمكن أن يحل محل جميع الحالات. لدينا مخرج مثل Bson و filter/sort/operator/options الخاصة بـ Driver، ويمكن استخدام MongoDB Driver مباشرة عند الحاجة.

الأربعة تدفقات التي يتم تناولها في الوثيقة الحالية.

استعلام Mongo العادي.

هذا هو التدفق الذي بدأ به هذه المكتبة. يتم تكوين الشروط والتنفيذ بالترتيب execute* → fields(...) → end() → find/findAll/count/delete/exists/atomicUpdate.

تجميع Driver-native.

إذا كنت بحاجة إلى التحكم مباشرة من المرحلة الأولى من pipeline أو استخدام ميزات التجميع الجديدة التي يوفرها Driver، استخدم تدفق aggregation().stage(Bson). يمكن لـ DSL تمرير المراحل التي يوفرها Driver مثل $score و $scoreFusion دون إعادة تنفيذها.

بحث Atlas

هذا هو التدفق الذي تم إضافته عند تطبيق Atlas Search على المشروع دون إدخال خدمة بحث منفصلة أولاً. يتم تكوين text و compound و score و highlight و sequence token بعد search(index).

بحث المتجهات

هذا هو التدفق الذي تم إضافته لربط البحث عن embedding داخل MongoDB. يتم تكوين query vector أو automated embedding و ANN/ENN و pre/post filter وخيارات embedding nested/array.

نموذج التنفيذ 1.0.0.
text

فصل الاستعلامات العادية عن Search/Vector هو لإخفاء قيود pipeline الفعلية في MongoDB. يحتوي $search و $vectorSearch على قيود المرحلة الأولى، والوظائف التي يجب أن يتحكم فيها المستدعي بالكامل يمكن أن تنزل إلى aggregation().

نطاق الاستخدام في المشاريع الفعلية

نمرر الأزواج التي لا تحتوي على شروط كـ null لتجميع شروط البحث الديناميكية حسب الشاشة.

نفصل البيانات و totalCount باستخدام PageStream للحفاظ على تدفق المعالجة الكبيرة في حالة ناشر reactive.

نقوم بإنشاء مهام آمنة من التنفيذ المكرر باستخدام atomicUpdate().upsertOne().document().setOnInsert(...).

نقوم بتخزين البيانات المجمعة وبيانات التكامل الخارجي بناءً على ID أو مفتاح العمل باستخدام bulk upsert.

نستخدم executeLookupAndCount لإرجاع نتائج lookup والعدد الإجمالي في pipeline واحد.

نستخدم sequence token من Atlas Search و automated embedding query من Vector Search.

تتصل مراحل التجميع الجديدة التي يوفرها Driver مباشرة بـ aggregation().stage(Bson) أو stage(Bson) لـ Search/Vector.

إذا كانت هناك حاجة إلى الذرية بين عمليات DSL، نحدد نطاق transaction باستخدام getTxJob(...) لـ ClientSession.

© 2026 بيولناريم. جميع الحقوق محفوظة.مقدمةسياسة معالجة المعلومات الشخصية