خصائص الوصول داخل حقل Jsonb ل Npgsql

يحتوي PostgreSQL على نوع بيانات Jsonb يسمح لك بإضافة خصائص إضافية إلى النموذج العلائقي القياسي مع إمكانية البحث من خلالها.


يمكن EntityFramework Core مع ملحق Npgsql سحب بيانات الحقل إلى نوع System.String


ومع ذلك ، للتصفية حسب خصائص Json عبر EF على مستوى الاستعلام ، يجب عليك استخدام SQL خالص ، وهو أمر غير مناسب للغاية ، لأنك تحتاج إلى الانتقال إلى التعيين (إذا لم يكن تلقائيًا) ، ابحث عن أسماء الحقول التي تتوافق مع خصائص النماذج ، ودعم هذا التسمية. فقدت المرونة التي يوفرها لنا ORM.


إذا كان ذلك يزعجك ، مثلي ، فمرحباً بك في القط


في نهاية المقال هناك رابط للمصدر!


دلالة على المهام


كمطور ، أريد أن أمتلك أداة للوصول إلى حقول Jsonb بهدف التصفية والفرز بواسطتها ، وهي:


  • ستكون متوافقة مع EntityFramework Core 2 (نستخدمها)
  • لن يتطلب منك كتابة SQL بنفسك أثناء العمل معها
  • ستعمل مع هيكل Json المسطح (داخل json لا يوجد سوى خصائص json)

سأضيف أنه يوجد Npgsql.Json.NET ، والذي يمكنه عرض قيم Json و Jsonb في أنواع CLR. لكي أكون أمينًا ، لا أفهم ما قد يكون عليه الأمر ، لأنه نظرًا لأننا كنا بحاجة إلى حقل Json في قاعدة بيانات علائقية ، فمن المحتمل أن يكون لدينا كيانات لها مجموعة ديناميكية من الحقول.


الخوارزمية لحل المشكلة


  1. حدد طريقة (أو طرق) تغطي احتياجاتنا.
  2. إنشاء مترجم يشارك في إنشاء كود SQL.
  3. المسمار كل شيء ل Npgsql.

قرار


أولاً ، نحدد طريقة. أريد أن يستخدم شيء مثل هذا:


 context.Entity.Where(x => JsonbMethods.Value<string>(x.JsonbField, "jsonPropertyName") == "value") 

لذلك ، ها هي طريقتنا:


 public static TSource Value<TSource>(object jsonbProperty, string jsonbPropertyName) { throw new NotSupportedException(); } 

لعدة ساعات كنت أختار مصادر EF Core و Npgsql وليس فقط للبحث عن طرق لتوسيع الوظائف الأساسية لتوليد SQL. حصلت على هذا المقال ، لكنني لم أحب طريقة المؤلف في طريقة توصيل المترجم ، لأنه يعيد تعريف الأداة القياسية ، مما يعني أنه يمكن أن يتعارض مع أداة أخرى مماثلة.
ونتيجة لذلك ، وصلت إلى مصدر Net Topology Suite. كل ما احتاجه من هناك كان وسيلة للاتصال بمترجم طريقة.


ولكن معظم الوقت الذي قضيته في توليد جزء sql كنت بحاجة.


هنا هو بناء الجملة المطلوب


tableAlias."JsonField"->>"insideProperty"


في البداية ، حاولت في المترجم إرجاع ColumnExpression. عند إنشائه ، تكون المعلمة الأولى هي اسم العمود (سلسلة). أنا فقط طهيها من المعلمات التي تأتي لي في الطريقة. بدأت ، فحص ، خطأ. اتضح أن ما أقوم به كاسم يتم لفه بعلامات اقتباس. نتيجة لذلك ، تحولت SQL إلى tableAlias.""JsonField"->>"insideProperty"" .


في التعليمات البرمجية المصدر للمولد ، وجدت طريقة VisitColumn التي كان بها هذا السلوك VisitColumn ولا تعتمد على أي معلمات. وهذا هو ، لم أستطع التأثير عليه. كان من الضروري البحث عن حل آخر.


ثم أنشأت Expression الخاص بي - JsonbPropertyAccessorExpression: Expression


يبقى لتجاوز أسلوب Accept لـ ISqlExpressionVisitor .


ولكن المشكلة هي أنه في هذه الواجهة لا توجد طريقة يمكن لمشغل مخصص أن يقطعها. ثم حدث لي أن زيارة ليس طريقة واحدة ، ولكن عدة. زرت VisitColumn لأول مرة ، والتي خلقت الوصول إلى tableAlias. عمود "JsonField" ، ثم VisitSqlFragment ، والذي رميت به "->>'insideFieldName'" .


لم أكن آمل ، لكنها نجحت. تقريبا.


عندما حاولت التصفية حسب النص من أجل المصادفة التامة ، لسبب ما ، تم tableAlias."JsonField"->>"insideProperty" = JSONB "value" عامل تصفية tableAlias."JsonField"->>"insideProperty" = JSONB "value" ، مما تسبب في حدوث خطأ ، لأنه من المستحيل إرسال النص إلى نوع JSONB إذا كان لا يحتوي على Json صالح . ولماذا أحتاج لقيادة شيء إلى شيء عندما أريد نصًا؟


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


بعد ذلك ، قمت بتعيين إلى CAST ، لأن طريقة Value عالمية ويمكن أن توجد أنواع مختلفة من البيانات في خصائص Json ، والتي تحتاج أيضًا إلى الفرز والتصفية.


نتيجة لذلك ، بدأت في إرجاع ExplicitCastExpression من المترجم الخاص بي ، والذي مررت فيه على Expression المخصص الخاص بي والنوع الذي تم تضمينه في الوسائط العامة لأسلوب Value .


عندما نظرت إلى SQL الناتج عند البحث حسب التاريخ ، وجدت أن القيمة المقارنة تم إلقاؤها على نوع الطابع الزمني. timestamp 'some date value' . وبعد ذلك بزغ فجر علي. المشكلة السابقة التي حلها مع عكاز ذهبت بعيدا في حد ذاته. عندما تم إرفاق الموصل في النص إلى حقل Json ، لم يعد المُنشئ يضيف تحويلًا صريحًا إلى JSONB ، لأن عملية المقارنة كانت تحتوي بالفعل على نص على اليسار ، وبشكل افتراضي ، يُرجع ملحق حقل Jsonb نوع Jsonb.


في النهاية


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


إذا أراد شخص ما توسيع المكتبة في شوكات ، والكتابة في رسالة شخصية ، فسأحاول المساعدة. حسنا ، أو رمي pullrequests.


هنا هو الرابط للمصدر

Source: https://habr.com/ru/post/ar463747/


All Articles