الخلفية
عمليات Pipeline هي واجهة طلب بحث جديدة لـ Cloud Firestore. توفّر هذه الواجهة وظائف طلب بحث متقدّمة تتضمّن عبارات معقّدة. يتيح إصدار Firestore Enterprise عمليات الربط بأسلوب علائقي من خلال الاستعلامات الفرعية المرتبطة. بخلاف العديد من قواعد بيانات NoSQL التي تتطلّب غالبًا إلغاء تسوية البيانات أو تنفيذ طلبات متعددة من جهة العميل، تتيح لك الاستعلامات الفرعية دمج البيانات وتجميعها من مجموعات أو مجموعات فرعية ذات صلة مباشرةً على الخادم.
الاستعلامات الفرعية هي عبارات تنفّذ مسارًا متداخلاً لكل مستند تتم معالجته بواسطة الاستعلام الخارجي. يتيح ذلك أنماطًا معقّدة لاسترداد البيانات، مثل جلب مستند مع عناصر المجموعة الفرعية ذات الصلة أو ربط البيانات المرتبطة منطقيًا من مجموعات جذرية مختلفة.
المفاهيم
يعرض هذا القسم المفاهيم الأساسية المتعلقة باستخدام طلبات البحث الفرعية لتنفيذ عمليات الربط في عمليات Pipeline.
الاستعلامات الفرعية كتعبيرات
الاستعلام الفرعي ليس مرحلة من المستوى الأعلى، بل هو تعبير يمكن استخدامه في أي مرحلة تقبل التعبيرات، مثل select(...) أو add_fields(...) أو where(...) أو sort(...).
تتيح Cloud Firestore ثلاثة أنواع من الاستعلامات الفرعية:
- الطلبات الفرعية للمصفوفات: يتم إنشاء مجموعة النتائج الكاملة للطلب الفرعي كمصفوفة من المستندات.
- الطلبات الفرعية العددية: يتم تقييمها إلى قيمة واحدة، مثل عدد أو متوسط أو حقل معيّن من مستند ذي صلة.
subcollection(...)طلبات البحث الفرعية: عمليات ربط مبسطة لعلاقة العنصر الرئيسي بالعناصر التابعة من نوع واحد إلى متعدد.
النطاق والمتغيرات
عند كتابة عملية ربط، يجب غالبًا أن يشير الاستعلام الفرعي المتداخل إلى حقول من المستند "الخارجي" (العنصر الرئيسي). لربط هذه النطاقات، يمكنك استخدام مرحلة let(...) (يُشار إليها باسم define(...) في بعض حِزم تطوير البرامج (SDK)) لتحديد المتغيرات في النطاق الرئيسي التي يمكن الرجوع إليها بعد ذلك في الاستعلام الفرعي باستخدام الدالة variable(...).
البنية
تقدّم الأقسام التالية نظرة عامة على صيغة تنفيذ عمليات الربط.
المرحلة let(...)
المرحلة let(...) (المشار إليها باسم define(...) في بعض حِزم SDK) هي مرحلة لا يتم فيها الفلترة، وتعمل على جلب البيانات بشكل صريح من النطاق الرئيسي إلى متغيّر مسمّى لاستخدامه في النطاقات المتداخلة اللاحقة.
Web
async function defineStageData() {
await setDoc(doc(collection(db, "Authors"), "author_123"), {
"id": "author_123",
"name": "Jane Austen"
});
}
Swift
func defineStageData() async throws { try await db.collection("authors").document("author_123").setData([ "id": "author_123", "name": "Jane Austen" ]) }
Kotlin
fun defineStageData() { val author = hashMapOf( "id" to "author_123", "name" to "Jane Austen", ) db.collection("Authors").document("author_123").set(author) }
Java
public void defineStageData() { Map<String, Object> author = new HashMap<>(); author.put("id", "author_123"); author.put("name", "Jane Austen"); db.collection("Authors").document("author_123").set(author); }
الاستعلامات الفرعية للمصفوفات
استعلام فرعي للمصفوفة هو حالة خاصة من استعلام فرعي للتعبير، ويحوّل مجموعة النتائج الكاملة للاستعلام الفرعي إلى مصفوفة. إذا عرضت الاستعلام الفرعي صفر صفوف، سيتم تقييمه إلى مصفوفة فارغة. ولا تعرض أبدًا مصفوفة null. تكون هذه الاستعلامات مفيدة عندما تكون النتائج الكاملة مطلوبة في النتيجة النهائية، مثلما يحدث عند إنشاء مجموعة متداخلة أو مرتبطة.
يمكن لطلبات البحث فلترة البيانات وترتيبها وتجميعها في طلب البحث الفرعي للحدّ أيضًا من كمية البيانات التي يجب جلبها وعرضها، ما يساعد في تقليل تكلفة طلب البحث. يتم احترام ترتيب الاستعلام الفرعي، ما يعني أنّ مرحلة sort(...) في الاستعلام الفرعي تتحكّم في ترتيب النتائج في المصفوفة النهائية.
استخدِم برنامج تضمين حزمة تطوير البرامج (SDK) toArrayExpression() لتحويل طلب بحث إلى مصفوفة.
Web
async function toArrayExpressionStageData() {
await setDoc(doc(collection(db, "Projects"), "project_1"), {
"id": "project_1",
"name": "Alpha Build"
});
await addDoc(collection(db, "Tasks"), {
"project_id": "project_1",
"title": "System Architecture"
});
await addDoc(collection(db, "Tasks"), {
"project_id": "project_1",
"title": "Database Schema Design"
});
}
الردّ
{
id: "project_1",
name: "Alpha Build",
taskTitles: [
"System Architecture", "Database Schema Design"
]
}
Swift
async function toArrayExpressionStageData() { await setDoc(doc(collection(db, "Projects"), "project_1"), { "id": "project_1", "name": "Alpha Build" }); await addDoc(collection(db, "Tasks"), { "project_id": "project_1", "title": "System Architecture" }); await addDoc(collection(db, "Tasks"), { "project_id": "project_1", "title": "Database Schema Design" }); }
الردّ
{ id: "project_1", name: "Alpha Build", taskTitles: [ "System Architecture", "Database Schema Design" ] }
Kotlin
fun toArrayExpressionData() { val project = hashMapOf( "id" to "project_1", "name" to "Alpha Build", ) db.collection("Projects").document("project_1").set(project) val task1 = hashMapOf( "project_id" to "project_1", "title" to "System Architecture", ) db.collection("Tasks").add(task1) val task2 = hashMapOf( "project_id" to "project_1", "title" to "Database Schema Design", ) db.collection("Tasks").add(task2) }
الردّ
{ id: "project_1", name: "Alpha Build", taskTitles: [ "System Architecture", "Database Schema Design" ] }
Java
public void toArrayExpressionData() { Map<String, Object> project = new HashMap<>(); project.put("id", "project_1"); project.put("name", "Alpha Build"); db.collection("Projects").document("project_1").set(project); Map<String, Object> task1 = new HashMap<>(); task1.put("project_id", "project_1"); task1.put("title", "System Architecture"); db.collection("Tasks").add(task1); Map<String, Object> task2 = new HashMap<>(); task2.put("project_id", "project_1"); task2.put("title", "Database Schema Design"); db.collection("Tasks").add(task2); }
الردّ
{ id: "project_1", name: "Alpha Build", taskTitles: [ "System Architecture", "Database Schema Design" ] }
الاستعلامات الفرعية العددية
يتم غالبًا استخدام الاستعلامات الفرعية العددية في مرحلة select(...) أو where(...) للسماح بالفلترة أو عرض نتيجة استعلام فرعي بدون تنفيذ الاستعلام الكامل مباشرةً.
سيتم تقييم طلب البحث الفرعي العددي الذي يعرض صفر نتائج إلى null نفسه، بينما سيؤدي طلب البحث الفرعي الذي يتم تقييمه إلى عناصر متعددة إلى حدوث خطأ في وقت التشغيل.
عندما ينتج طلب فرعي عددي حقلاً واحدًا فقط لكل نتيجة، يتم رفع الحقل ليكون النتيجة ذات المستوى الأعلى للطلب الفرعي. يحدث ذلك عادةً عندما ينتهي الاستعلام الفرعي بـ select(field("user_name")) أو aggregate(countAll().as("total")) حيث يكون مخطط الاستعلام الفرعي عبارة عن حقل واحد فقط. بخلاف ذلك، عندما يمكن أن ينتج طلب البحث الفرعي حقولاً متعددة، يتم تضمينها في خريطة.
استخدِم برنامج تضمين حزمة تطوير البرامج (SDK) toScalarExpression() لتحويل طلب بحث إلى تعبير عددي.
Web
async function toScalarExpressionStageData() {
await setDoc(doc(collection(db, "Authors"), "author_202"), {
"id": "author_202",
"name": "Charles Dickens"
});
await addDoc(collection(db, "Books"), {
"author_id": "author_202",
"title": "Great Expectations",
"rating": 4.8
});
await addDoc(collection(db, "Books"), {
"author_id": "author_202",
"title": "Oliver Twist",
"rating": 4.5
});
}
الردّ
{
"id": "author_202",
"name": "Charles Dickens",
"averageBookRating": 4.65
}
Swift
try await db.collection("authors").document("author_202").setData([ "id": "author_202", "name": "Charles Dickens" ]) try await db.collection("books").document().setData([ "author_id": "author_202", "title": "Great Expectations", "rating": 4.8 ]) try await db.collection("books").document().setData([ "author_id": "author_202", "title": "Oliver Twist", "rating": 4.5 ])
الردّ
{ "id": "author_202", "name": "Charles Dickens", "averageBookRating": 4.65 }
Kotlin
fun toScalarExpressionData() { val author = hashMapOf( "id" to "author_202", "name" to "Charles Dickens", ) db.collection("Authors").document("author_202").set(author) val book1 = hashMapOf( "author_id" to "author_202", "title" to "Great Expectations", "rating" to 4.8, ) db.collection("Books").add(book1) val book2 = hashMapOf( "author_id" to "author_202", "title" to "Oliver Twist", "rating" to 4.5, ) db.collection("Books").add(book2) }
الردّ
{ "id": "author_202", "name": "Charles Dickens", "averageBookRating": 4.65 }
Java
public void toScalarExpressionData() { Map<String, Object> author = new HashMap<>(); author.put("id", "author_202"); author.put("name", "Charles Dickens"); db.collection("Authors").document("author_202").set(author); Map<String, Object> book1 = new HashMap<>(); book1.put("author_id", "author_202"); book1.put("title", "Great Expectations"); book1.put("rating", 4.8); db.collection("Books").add(book1); Map<String, Object> book2 = new HashMap<>(); book2.put("author_id", "author_202"); book2.put("title", "Oliver Twist"); book2.put("rating", 4.5); db.collection("Books").add(book2); }
الردّ
{ "id": "author_202", "name": "Charles Dickens", "averageBookRating": 4.65 }
subcollection(...) الاستعلامات الفرعية
على الرغم من أنّ مرحلة إدخال
subcollection(...) تُقدَّم كمرحلة، فإنّها تتيح إجراء عمليات ربط على نموذج البيانات الهرمي الخاص بـ Cloud Firestore. في النموذج الهرمي، غالبًا ما تحتاج طلبات البحث إلى استرداد مستند إلى جانب البيانات من مجموعاته الفرعية. على الرغم من أنّه يمكنك تحقيق ذلك باستخدام مرحلة إدخال collection_group(...) متبوعة بفلتر على المرجع الرئيسي، إلا أنّ subcollection(...) يوفّر صيغة أكثر اختصارًا.
بخلاف شرط الربط الضمني، يعمل هذا الإجراء بشكل مشابه لاستعلام فرعي عن مصفوفة، ويعرض نتيجة فارغة إذا لم تتم مطابقة أي مستندات، حتى إذا لم تكن المجموعة المتداخلة متوفّرة.
وهي في الأساس تجميل لغوي: تستخدم تلقائيًا __name__ للمستند في النطاق الخارجي كمفتاح ربط لحل العلاقة الهرمية. وهذا يجعلها الطريقة المفضّلة لإجراء عمليات البحث في المجموعات المرتبطة بعلاقة العنصر الرئيسي والعنصر الثانوي.
أفضل الممارسات
- إدارة الذاكرة باستخدام
toArrayExpression(): يجب توخّي الحذر عند استخدام طلبات البحث الفرعيةtoArrayExpression()، لأنّ إنشاء عدد كبير من المستندات قد يؤدي إلى استنفاد الحد الأقصى المسموح به لذاكرة طلب البحث (128 ميغابايت). لحلّ هذه المشكلة، استخدِمselect(...)ضمن طلب البحث الفرعي لعرض الحقول الضرورية فقط، وطبِّق فلاترwhere(...)للحدّ من عدد المستندات المعروضة. ننصحك باستخدامlimit(...)إذا كان ذلك مناسبًا للحدّ من عدد المستندات التي تعرضها الاستعلامات الفرعية. - الفهرسة: تأكَّد من فهرسة الحقول المستخدَمة في عبارة
where(...)الخاصة باستعلام فرعي. تعتمد عمليات الربط ذات الأداء الجيد على إمكانية إجراء عمليات بحث في الفهرس بدلاً من عمليات البحث الكاملة في الجدول.
لمزيد من أفضل الممارسات المتعلّقة بالطلبات، يُرجى الرجوع إلى الدليل الذي يغطّي تحسين الطلبات.
القيود
subcollection(...)النطاق: لا تتوفّر مرحلة الإدخالsubcollection(...)إلا ضمن الاستعلامات الفرعية، لأنّها تتطلّب سياق مستند رئيسي لحل العلاقة الهرمية وإجراء عملية الربط.- عمق التضمين: يمكن تضمين الاستعلامات الفرعية بما يصل إلى 20 طبقة.
- استخدام الذاكرة: ينطبق الحدّ الأقصى البالغ 128 ميغابايت على البيانات المادية في طلب البحث بأكمله، بما في ذلك جميع المستندات التي تم ربطها.