الحصول على البيانات باستخدام عمليات Firestore Pipeline

الخلفية

عمليات خطوط الإنتاج هي واجهة طلب بحث جديدة لـ Cloud Firestore. توفّر هذه الواجهة وظائف طلب بحث متقدّمة تتضمّن عبارات معقّدة. ويضيف أيضًا دعمًا للعديد من الدوال الجديدة، مثل min, max, substring, regex_match وarray_contains_all.

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

البدء

لتثبيت حِزم SDK الخاصة بالعميل وإعدادها، يُرجى الرجوع إلى التعليمات الواردة في الأدلة التالية:

البنية

تقدّم الأقسام التالية نظرة عامة على بنية عمليات Pipeline.

المفاهيم

أحد الاختلافات الملحوظة في عمليات مسار المعالجة هو تقديم ترتيب "المرحلة" الصريح. ويتيح ذلك إمكانية التعبير عن طلبات بحث أكثر تعقيدًا. ومع ذلك، يمثّل ذلك انحرافًا ملحوظًا عن واجهة طلب البحث الحالية (العمليات الأساسية) التي كان ترتيب المراحل فيها ضمنيًا. إليك مثالاً على عملية Pipeline:

Web

const pipeline = db.pipeline()
  // Step 1: Start a query with collection scope
  .collection("cities")
  // Step 2: Filter the collection
  .where(field("population").greaterThan(100000))
  // Step 3: Sort the remaining documents
  .sort(field("name").ascending())
  // Step 4: Return the top 10. Note applying the limit earlier in the
  // pipeline would have unintentional results.
  .limit(10);
Swift
let pipeline = db.pipeline()
  // Step 1: Start a query with collection scope
  .collection("cities")
  // Step 2: Filter the collection
  .where(Field("population").greaterThan(100000))
  // Step 3: Sort the remaining documents
  .sort([Field("name").ascending()])
  // Step 4: Return the top 10. Note applying the limit earlier in the pipeline would have
  // unintentional results.
  .limit(10)

Kotlin

val pipeline = db.pipeline()
    // Step 1: Start a query with collection scope
    .collection("cities")
    // Step 2: Filter the collection
    .where(field("population").greaterThan(100000))
    // Step 3: Sort the remaining documents
    .sort(field("name").ascending())
    // Step 4: Return the top 10. Note applying the limit earlier in the pipeline would have
    // unintentional results.
    .limit(10)

Java

Pipeline pipeline = db.pipeline()
    // Step 1: Start a query with collection scope
    .collection("cities")
    // Step 2: Filter the collection
    .where(field("population").greaterThan(100000))
    // Step 3: Sort the remaining documents
    .sort(field("name").ascending())
    // Step 4: Return the top 10. Note applying the limit earlier in the pipeline would have
    // unintentional results.
    .limit(10);
Python
from google.cloud.firestore_v1.pipeline_expressions import Field

pipeline = (
    client.pipeline()
    .collection("cities")
    .where(Field.of("population").greater_than(100_000))
    .sort(Field.of("name").ascending())
    .limit(10)
)

الإعداد

تتضمّن عمليات خطوط الأنابيب بنية مألوفة جدًا مستمدة من طلبات البحث Cloud Firestore الحالية. للبدء، عليك تهيئة طلب بحث من خلال كتابة ما يلي:

Web

const { getFirestore } = require("firebase/firestore");
const { execute } = require("firebase/firestore/pipelines");
const database = getFirestore(app, "enterprise");
const pipeline = database.pipeline();
Swift
let firestore = Firestore.firestore(database: "enterprise")
let pipeline = firestore.pipeline()

Kotlin

val firestore = Firebase.firestore("enterprise")
val pipeline = firestore.pipeline()

Java

FirebaseFirestore firestore = FirebaseFirestore.getInstance("enterprise");
PipelineSource pipeline = firestore.pipeline();
Python
firestore_client = firestore.client(default_app, "your-new-enterprise-database")
pipeline = firestore_client.pipeline()

البنية

هناك بعض المصطلحات المهمة التي يجب فهمها عند إنشاء عمليات Pipeline، وهي: المراحل والعبارات والدوال.

مثال يوضّح المراحل والعبارات في طلب بحث

المراحل: قد تتألف عملية البيع من مرحلة واحدة أو أكثر. من الناحية المنطقية، تمثّل هذه المراحل سلسلة الخطوات التي يتم اتّخاذها لتنفيذ طلب البحث. ملاحظة: من الناحية العملية، قد يتم تنفيذ المراحل بترتيب مختلف لتحسين الأداء. ومع ذلك، لا يؤدي ذلك إلى تعديل الغرض من الطلب أو صحته.

التعبيرات: غالبًا ما تقبل المراحل تعبيرًا يتيح لك التعبير عن طلبات بحث أكثر تعقيدًا. قد تكون الصيغة بسيطة وتتألف من دالة واحدة مثل eq("a", 1). يمكنك أيضًا التعبير عن صيغ أكثر تعقيدًا من خلال تضمين صيغ مثل and(eq("a", 1), eq("b", 2)).

المراجع إلى الحقول مقابل المراجع إلى الثوابت

تتيح عمليات خطوط النقل استخدام تعبيرات معقّدة. وبالتالي، قد يكون من الضروري التمييز بين ما إذا كانت القيمة تمثّل حقل أو ثابت. راجع المثال التالي:

Web

const pipeline = db.pipeline()
  .collection("cities")
  .where(field("name").equal(constant("Toronto")));
Swift
let pipeline = db.pipeline()
  .collection("cities")
  .where(Field("name").equal(Constant("Toronto")))

Kotlin

val pipeline = db.pipeline()
    .collection("cities")
    .where(field("name").equal(constant("Toronto")))

Java

Pipeline pipeline = db.pipeline()
    .collection("cities")
    .where(field("name").equal(constant("Toronto")));
Python
from google.cloud.firestore_v1.pipeline_expressions import Field, Constant

pipeline = (
    client.pipeline()
    .collection("cities")
    .where(Field.of("name").equal(Constant.of("Toronto")))
)

المسارح

مراحل الإدخال

تمثّل مرحلة الإدخال المرحلة الأولى من طلب البحث. تحدّد هذه السمة المجموعة الأولية من المستندات التي تبحث فيها. بالنسبة إلى عمليات Pipeline، يشبه ذلك إلى حد كبير الاستعلامات الحالية، حيث تبدأ معظم الاستعلامات إما بمرحلة collection(...) أو collection_group(...). مرحلتا الإدخال الجديدتان هما database() وdocuments(...)، حيث تتيح database() عرض جميع المستندات في قاعدة البيانات، بينما تعمل documents(...) بشكل مطابق للقراءة المجمّعة.

Web

let results;

// Return all restaurants in San Francisco
results = await execute(db.pipeline().collection("cities/sf/restaurants"));

// Return all restaurants
results = await execute(db.pipeline().collectionGroup("restaurants"));

// Return all documents across all collections in the database (the entire database)
results = await execute(db.pipeline().database());

// Batch read of 3 documents
results = await execute(db.pipeline().documents([
  doc(db, "cities", "SF"),
  doc(db, "cities", "DC"),
  doc(db, "cities", "NY")
]));
Swift
var results: Pipeline.Snapshot

// Return all restaurants in San Francisco
results = try await db.pipeline().collection("cities/sf/restaurants").execute()

// Return all restaurants
results = try await db.pipeline().collectionGroup("restaurants").execute()

// Return all documents across all collections in the database (the entire database)
results = try await db.pipeline().database().execute()

// Batch read of 3 documents
results = try await db.pipeline().documents([
  db.collection("cities").document("SF"),
  db.collection("cities").document("DC"),
  db.collection("cities").document("NY")
]).execute()

Kotlin

var results: Task<Pipeline.Snapshot>

// Return all restaurants in San Francisco
results = db.pipeline().collection("cities/sf/restaurants").execute()

// Return all restaurants
results = db.pipeline().collectionGroup("restaurants").execute()

// Return all documents across all collections in the database (the entire database)
results = db.pipeline().database().execute()

// Batch read of 3 documents
results = db.pipeline().documents(
    db.collection("cities").document("SF"),
    db.collection("cities").document("DC"),
    db.collection("cities").document("NY")
).execute()

Java

Task<Pipeline.Snapshot> results;

// Return all restaurants in San Francisco
results = db.pipeline().collection("cities/sf/restaurants").execute();

// Return all restaurants
results = db.pipeline().collectionGroup("restaurants").execute();

// Return all documents across all collections in the database (the entire database)
results = db.pipeline().database().execute();

// Batch read of 3 documents
results = db.pipeline().documents(
    db.collection("cities").document("SF"),
    db.collection("cities").document("DC"),
    db.collection("cities").document("NY")
).execute();
Python
# Return all restaurants in San Francisco
results = client.pipeline().collection("cities/sf/restaurants").execute()

# Return all restaurants
results = client.pipeline().collection_group("restaurants").execute()

# Return all documents across all collections in the database (the entire database)
results = client.pipeline().database().execute()

# Batch read of 3 documents
results = (
    client.pipeline()
    .documents(
        client.collection("cities").document("SF"),
        client.collection("cities").document("DC"),
        client.collection("cities").document("NY"),
    )
    .execute()
)

كما هو الحال مع جميع المراحل الأخرى، لا يكون ترتيب النتائج من مراحل الإدخال هذه ثابتًا. يجب دائمًا إضافة عامل التشغيل sort(...) إذا كان الترتيب محدّدًا مطلوبًا.

المكان

تعمل المرحلة where(...) كعملية فلترة تقليدية على المستندات التي تم إنشاؤها من المرحلة السابقة، وهي تشبه إلى حد كبير بنية "where" الحالية للاستعلامات الحالية. يتم استبعاد أي مستند لا يتم فيه تقييم تعبير معيّن إلى قيمة غير true من المستندات التي يتم عرضها.

يمكن ربط عدّة عبارات where(...) معًا، ويمكن أن تعمل كعبارة and(...). على سبيل المثال، طلبَا البحث التاليان متكافئان منطقيًا ويمكن استخدامهما بالتبادل.

Web

let results;

results = await execute(db.pipeline().collection("books")
  .where(field("rating").equal(5))
  .where(field("published").lessThan(1900))
);

results = await execute(db.pipeline().collection("books")
  .where(and(field("rating").equal(5), field("published").lessThan(1900)))
);
Swift
var results: Pipeline.Snapshot

results = try await db.pipeline().collection("books")
  .where(Field("rating").equal(5))
  .where(Field("published").lessThan(1900))
  .execute()

results = try await db.pipeline().collection("books")
  .where(Field("rating").equal(5) && Field("published").lessThan(1900))
  .execute()

Kotlin

var results: Task<Pipeline.Snapshot>

results = db.pipeline().collection("books")
    .where(field("rating").equal(5))
    .where(field("published").lessThan(1900))
    .execute()

results = db.pipeline().collection("books")
    .where(Expression.and(field("rating").equal(5),
      field("published").lessThan(1900)))
    .execute()

Java

Task<Pipeline.Snapshot> results;

results = db.pipeline().collection("books")
    .where(field("rating").equal(5))
    .where(field("published").lessThan(1900))
    .execute();

results = db.pipeline().collection("books")
    .where(Expression.and(
        field("rating").equal(5),
        field("published").lessThan(1900)
    ))
    .execute();
Python
from google.cloud.firestore_v1.pipeline_expressions import And, Field

results = (
    client.pipeline()
    .collection("books")
    .where(Field.of("rating").equal(5))
    .where(Field.of("published").less_than(1900))
    .execute()
)

results = (
    client.pipeline()
    .collection("books")
    .where(And(Field.of("rating").equal(5), Field.of("published").less_than(1900)))
    .execute()
)

اختيار الحقول أو إضافتها أو إزالتها

تتيح لك select(...) وadd_fields(...) وremove_fields(...) تعديل الحقول التي يتم عرضها من مرحلة سابقة. ويُشار إلى هذه المراحل الثلاث بشكل عام باسم مراحل العرض.

يتيح لك select(...) وadd_fields(...) تحديد نتيجة تعبير لاسم حقل يوفّره المستخدم. سيؤدي التعبير الذي ينتج عنه خطأ إلى ظهور القيمة null. لن تعرض select(...) سوى المستندات التي تتضمّن أسماء الحقول المحدّدة، بينما توسّع add_fields(...) مخطط المرحلة السابقة (ما قد يؤدي إلى استبدال القيم بأسماء حقول متطابقة).

تسمح السمة remove_fields(...) بتحديد مجموعة من الحقول المطلوب إزالتها من المرحلة السابقة. تحديد أسماء حقول غير متوفّرة هو عملية غير فعّالة.

راجِع القسم تقييد الحقول التي سيتم عرضها أدناه، ولكن بشكل عام، يكون استخدام مرحلة كهذه لتقييد النتيجة بالحقول المطلوبة فقط في العميل مفيدًا في تقليل التكلفة ووقت الاستجابة لمعظم طلبات البحث.

التجميع / التمييز

تتيح لك المرحلة aggregate(...) إجراء سلسلة من عمليات التجميع على مستندات الإدخال. يتم تجميع جميع المستندات معًا تلقائيًا، ولكن يمكن تقديم وسيطة grouping اختيارية، ما يسمح بتجميع المستندات المُدخَلة في حِزم مختلفة.

Web

const results = await execute(db.pipeline()
  .collection("books")
  .aggregate(
    field("rating").average().as("avg_rating")
  )
  .distinct(field("genre"))
);
Swift
let results = try await db.pipeline()
  .collection("books")
  .aggregate([
    Field("rating").average().as("avg_rating")
  ], groups: [
    Field("genre")
  ])
  .execute()

Kotlin

val results = db.pipeline()
    .collection("books")
    .aggregate(
        AggregateStage
            .withAccumulators(AggregateFunction.average("rating").alias("avg_rating"))
            .withGroups(field("genre"))
    )
    .execute()

Java

Task<Pipeline.Snapshot> results = db.pipeline()
    .collection("books")
    .aggregate(AggregateStage
        .withAccumulators(
            AggregateFunction.average("rating").alias("avg_rating"))
        .withGroups(field("genre")))
    .execute();
Python
from google.cloud.firestore_v1.pipeline_expressions import Field

results = (
    client.pipeline()
    .collection("books")
    .aggregate(
        Field.of("rating").average().as_("avg_rating"), groups=[Field.of("genre")]
    )
    .execute()
)

عندما لا يتم تحديد groupings، ستنتج هذه المرحلة مستندًا واحدًا فقط، وإلا سيتم إنشاء مستند لكل مجموعة فريدة من قيم groupings.

distinct(...) هي مرحلة بسيطة لتجميع البيانات تتيح إنشاء groupings الفريد فقط بدون أي أدوات تجميع. ويتشابه سلوكه تمامًا مع سلوك aggregate(...) في جميع الجوانب الأخرى. في ما يلي مثال:

Web

const results = await execute(db.pipeline()
  .collection("books")
  .distinct(
    field("author").toUpper().as("author"),
    field("genre")
  )
);
Swift
let results = try await db.pipeline()
  .collection("books")
  .distinct([
    Field("author").toUpper().as("author"),
    Field("genre")
  ])
  .execute()

Kotlin

val results = db.pipeline()
    .collection("books")
    .distinct(
        field("author").toUpper().alias("author"),
        field("genre")
    )
    .execute()

Java

Task<Pipeline.Snapshot> results = db.pipeline()
    .collection("books")
    .distinct(
        field("author").toUpper().alias("author"),
        field("genre")
    )
    .execute();
Python
from google.cloud.firestore_v1.pipeline_expressions import Field

results = (
    client.pipeline()
    .collection("books")
    .distinct(Field.of("author").to_upper().as_("author"), "genre")
    .execute()
)

الدوال

الدوال هي لبنة أساسية لإنشاء التعبيرات وطلبات البحث المعقّدة. للحصول على قائمة كاملة بالدوال مع أمثلة، يُرجى الرجوع إلى مرجع الدوال. كتذكير سريع، إليك بنية طلب البحث العادي:

مثال يوضّح المراحل والدوال في طلب بحث

تقبل العديد من المراحل عبارات تحتوي على دالة واحدة أو أكثر. يمكن العثور على الاستخدام الأكثر شيوعًا للدالة في مرحلتَي where(...) وselect(...). هناك نوعان رئيسيان من الدوال يجب أن تكون على دراية بهما:

Web

let results;

// Type 1: Scalar (for use in non-aggregation stages)
// Example: Return the min store price for each book.
results = await execute(db.pipeline().collection("books")
  .select(field("current").logicalMinimum(field("updated")).as("price_min"))
);

// Type 2: Aggregation (for use in aggregate stages)
// Example: Return the min price of all books.
results = await execute(db.pipeline().collection("books")
  .aggregate(field("price").minimum().as("min_price"))
);
Swift
var results: Pipeline.Snapshot

// Type 1: Scalar (for use in non-aggregation stages)
// Example: Return the min store price for each book.
results = try await db.pipeline().collection("books")
  .select([
    Field("current").logicalMinimum(["updated"]).as("price_min")
  ])
  .execute()

// Type 2: Aggregation (for use in aggregate stages)
// Example: Return the min price of all books.
results = try await db.pipeline().collection("books")
  .aggregate([Field("price").minimum().as("min_price")])
  .execute()

Kotlin

var results: Task<Pipeline.Snapshot>

// Type 1: Scalar (for use in non-aggregation stages)
// Example: Return the min store price for each book.
results = db.pipeline().collection("books")
    .select(
        field("current").logicalMinimum("updated").alias("price_min")
    )
    .execute()

// Type 2: Aggregation (for use in aggregate stages)
// Example: Return the min price of all books.
results = db.pipeline().collection("books")
    .aggregate(AggregateFunction.minimum("price").alias("min_price"))
    .execute()

Java

Task<Pipeline.Snapshot> results;

// Type 1: Scalar (for use in non-aggregation stages)
// Example: Return the min store price for each book.
results = db.pipeline().collection("books")
    .select(
        field("current").logicalMinimum("updated").alias("price_min")
    )
    .execute();

// Type 2: Aggregation (for use in aggregate stages)
// Example: Return the min price of all books.
results = db.pipeline().collection("books")
    .aggregate(AggregateFunction.minimum("price").alias("min_price"))
    .execute();
Python
from google.cloud.firestore_v1.pipeline_expressions import Field

# Type 1: Scalar (for use in non-aggregation stages)
# Example: Return the min store price for each book.
results = (
    client.pipeline()
    .collection("books")
    .select(
        Field.of("current").logical_minimum(Field.of("updated")).as_("price_min")
    )
    .execute()
)

# Type 2: Aggregation (for use in aggregate stages)
# Example: Return the min price of all books.
results = (
    client.pipeline()
    .collection("books")
    .aggregate(Field.of("price").minimum().as_("min_price"))
    .execute()
)

الحدود

في معظم الحالات، لا يفرض إصدار Enterprise قيودًا على شكل طلب البحث. بعبارة أخرى، لا يقتصر الأمر على عدد صغير من القيم في طلب بحث IN أو OR. بدلاً من ذلك، هناك حدّان أساسيان يجب أن تكون على دراية بهما:

  • الموعد النهائي: 60 ثانية (كما هو الحال في الإصدار Standard).
  • استخدام الذاكرة: حدّ يبلغ 128 ميغابايت على كمية البيانات المادية أثناء تنفيذ طلب البحث

الأخطاء

قد تواجه طلبات بحث غير ناجحة لعدة أسباب. إليك رابط يؤدي إلى الأخطاء الشائعة والإجراءات المرتبطة التي يمكنك اتّخاذها:

رمز الخطأ الإجراء
DEADLINE_EXCEEDED يتجاوز طلب البحث الذي تنفّذه مهلة 60 ثانية ويتطلّب تحسينًا إضافيًا. يُرجى الاطّلاع على قسم "الأداء" للحصول على نصائح. إذا لم تتمكّن من تحديد السبب الجذري للمشكلة، تواصَل مع الفريق.
RESOURCE_EXHAUSTED يتجاوز طلب البحث الذي تنفّذه حدود الذاكرة ويتطلّب تحسينًا إضافيًا. يُرجى الاطّلاع على قسم "الأداء" للحصول على نصائح. إذا لم تتمكّن من تحديد السبب الجذري للمشكلة، تواصَل مع الفريق.
INTERNAL التواصل مع الفريق للحصول على الدعم

الأداء

على عكس طلبات البحث الحالية، لا تتطلّب عمليات Pipeline توفّر فهرس دائمًا. وهذا يعني أنّ طلب البحث يمكن أن يستغرق وقتًا أطول مقارنةً بطلبات البحث الحالية التي كانت ستتعذّر على الفور بسبب خطأ FAILED_PRECONDITION في الفهرس غير المتوفّر. لتحسين أداء عمليات Pipeline، يمكنك اتّخاذ بعض الخطوات.

إنشاء الفهارس

الفهرس المستخدَم

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

إنشاء الفهارس

يمكنك اتّباع مستندات إدارة الفهرس الحالية لإنشاء الفهارس. قبل إنشاء فهرس، يُرجى الاطّلاع على أفضل الممارسات العامة المتعلّقة بالفهارس في Cloud Firestore. لضمان إمكانية استخدام الفهارس في طلب البحث، اتّبِع أفضل الممارسات لإنشاء فهارس تتضمّن حقولاً بالترتيب التالي:

  1. جميع الحقول التي سيتم استخدامها في فلاتر المساواة (بأي ترتيب)
  2. جميع الحقول التي سيتم ترتيبها (بالترتيب نفسه)
  3. الحقول التي سيتم استخدامها في فلاتر النطاق أو عدم المساواة بترتيب تنازلي حسب اختيارية قيود الطلب

على سبيل المثال، بالنسبة إلى طلب البحث التالي،

Web

const results = await execute(db.pipeline()
  .collection("books")
  .where(field("published").lessThan(1900))
  .where(field("genre").equal("Science Fiction"))
  .where(field("rating").greaterThan(4.3))
  .sort(field("published").descending())
);
Swift
let results = try await db.pipeline()
  .collection("books")
  .where(Field("published").lessThan(1900))
  .where(Field("genre").equal("Science Fiction"))
  .where(Field("rating").greaterThan(4.3))
  .sort([Field("published").descending()])
  .execute()

Kotlin

val results = db.pipeline()
    .collection("books")
    .where(field("published").lessThan(1900))
    .where(field("genre").equal("Science Fiction"))
    .where(field("rating").greaterThan(4.3))
    .sort(field("published").descending())
    .execute()

Java

Task<Pipeline.Snapshot> results = db.pipeline()
    .collection("books")
    .where(field("published").lessThan(1900))
    .where(field("genre").equal("Science Fiction"))
    .where(field("rating").greaterThan(4.3))
    .sort(field("published").descending())
    .execute();
Python
from google.cloud.firestore_v1.pipeline_expressions import Field

results = (
    client.pipeline()
    .collection("books")
    .where(Field.of("published").less_than(1900))
    .where(Field.of("genre").equal("Science Fiction"))
    .where(Field.of("rating").greater_than(4.3))
    .sort(Field.of("published").descending())
    .execute()
)

الفهرس المقترَح هو فهرس نطاق مجموعة على books لـ (genre [...], published DESC, avg_rating DESC).

كثافة الفهرس

تتيح Cloud Firestore استخدام فهارس متفرقة وغير متفرقة. لمزيد من المعلومات، اطّلِع على كثافة الفهرسة.

طلبات البحث المغطّاة والفهارس الثانوية

يمكن لـ Cloud Firestore تخطّي جلب المستند الكامل وعرض النتائج من الفهرس فقط إذا كانت جميع الحقول التي يتم عرضها متوفرة في فهرس ثانوي. يؤدي ذلك عادةً إلى تحسين كبير في وقت الاستجابة (والتكلفة). باستخدام نموذج الاستعلام أدناه:

Web

const results = await execute(db.pipeline()
  .collection("books")
  .where(field("category").like("%fantasy%"))
  .where(field("title").exists())
  .where(field("author").exists())
  .select(field("title"), field("author"))
);
Swift
let results = try await db.pipeline()
  .collection("books")
  .where(Field("category").like("%fantasy%"))
  .where(Field("title").exists())
  .where(Field("author").exists())
  .select([Field("title"), Field("author")])
  .execute()

Kotlin

val results = db.pipeline()
    .collection("books")
    .where(field("category").like("%fantasy%"))
    .where(field("title").exists())
    .where(field("author").exists())
    .select(field("title"), field("author"))
    .execute()

Java

Task<Pipeline.Snapshot> results = db.pipeline()
    .collection("books")
    .where(field("category").like("%fantasy%"))
    .where(field("title").exists())
    .where(field("author").exists())
    .select(field("title"), field("author"))
    .execute();
Python
from google.cloud.firestore_v1.pipeline_expressions import Field

results = (
    client.pipeline()
    .collection("books")
    .where(Field.of("category").like("%fantasy%"))
    .where(Field.of("title").exists())
    .where(Field.of("author").exists())
    .select("title", "author")
    .execute()
)

إذا كانت قاعدة البيانات تتضمّن فهرس نطاق مجموعة على books لـ (category [...], title [...], author [...])، يمكنها تجنُّب جلب أي بيانات من المستندات الرئيسية نفسها. في هذه الحالة، لا يهم الترتيب في الفهرس، ويتم استخدام [...] للإشارة إلى ذلك.

تقييد الحقول المطلوب عرضها

يعرض طلب البحث Cloud Firestore تلقائيًا جميع الحقول في المستند، وهو ما يشبه SELECT * في الأنظمة التقليدية. إذا كان تطبيقك يحتاج فقط إلى مجموعة فرعية من الحقول، يمكن استخدام مرحلتَي select(...) أو restrict(...) لإجراء هذه الفلترة من جهة الخادم. سيؤدي ذلك إلى تقليل حجم الاستجابة (ما يؤدي إلى خفض تكلفة نقل البيانات خارج الشبكة) بالإضافة إلى تحسين وقت الاستجابة.

أدوات تحرّي الخلل وإصلاحه

شرح الطلب

تتيح لك ميزة "شرح طلب البحث" إمكانية الاطّلاع على مقاييس التنفيذ وتفاصيل حول الفهارس المستخدَمة.

المقاييس

عمليات خط الإنتاج إذا كانت مدمجة بالكامل مع مقاييس Cloud Firestore الحالية

المشاكل والقيود المعروفة

الفهارس المتخصّصة

لا تتيح عمليات خطوط الإنتاج بعد array-contains وvector أنواع الفهرس الحالية. بدلاً من رفض هذه الطلبات فقط، سيحاول Cloud Firestore استخدام فهارس ascending وdescending أخرى حالية. من المتوقّع أن تكون عمليات "مسار البيانات" التي تتضمّن تعبيرات array_contains أو find_nearest أبطأ من العمليات المشابهة الحالية خلال المعاينة الخاصة بسبب ذلك.

الحدود الفاصلة للصفحات

لا تتوفّر إمكانية تقسيم مجموعة النتائج إلى صفحات بسهولة أثناء المعاينة الخاصة. يمكن حلّ هذه المشكلة من خلال ربط مراحل where(...) وsort(...) المكافئة كما هو موضّح أدناه.

Web

// Existing pagination via `startAt()`
const q =
  query(collection(db, "cities"), orderBy("population"), startAt(1000000));

// Private preview workaround using pipelines
const pageSize = 2;
const pipeline = db.pipeline()
  .collection("cities")
  .select("name", "population", "__name__")
  .sort(field("population").descending(), field("__name__").ascending());

// Page 1 results
let snapshot = await execute(pipeline.limit(pageSize));

// End of page marker
const lastDoc = snapshot.results[snapshot.results.length - 1];

// Page 2 results
snapshot = await execute(
  pipeline
    .where(
      or(
        and(
          field("population").equal(lastDoc.get("population")),
          field("__name__").greaterThan(lastDoc.ref)
        ),
        field("population").lessThan(lastDoc.get("population"))
      )
    )
    .limit(pageSize)
);
Swift
// Existing pagination via `start(at:)`
let query = db.collection("cities").order(by: "population").start(at: [1000000])

// Private preview workaround using pipelines
let pipeline = db.pipeline()
  .collection("cities")
  .where(Field("population").greaterThanOrEqual(1000000))
  .sort([Field("population").descending()])

Kotlin

// Existing pagination via `startAt()`
val query = db.collection("cities").orderBy("population").startAt(1000000)

// Private preview workaround using pipelines
val pipeline = db.pipeline()
    .collection("cities")
    .where(field("population").greaterThanOrEqual(1000000))
    .sort(field("population").descending())

Java

// Existing pagination via `startAt()`
Query query = db.collection("cities").orderBy("population").startAt(1000000);

// Private preview workaround using pipelines
Pipeline pipeline = db.pipeline()
    .collection("cities")
    .where(field("population").greaterThanOrEqual(1000000))
    .sort(field("population").descending());
Python
from google.cloud.firestore_v1.pipeline_expressions import Field

# Existing pagination via `start_at()`
query = (
    client.collection("cities")
    .order_by("population")
    .start_at({"population": 1_000_000})
)

# Private preview workaround using pipelines
pipeline = (
    client.pipeline()
    .collection("cities")
    .where(Field.of("population").greater_than_or_equal(1_000_000))
    .sort(Field.of("population").descending())
)

التوافق مع المحاكي

لا يتيح المحاكي عمليات Pipeline بعد.

التوافق مع ميزة "في الوقت الفعلي" وبلا إنترنت

لا تتوفّر بعد إمكانات الوقت الفعلي وبلا إنترنت لعمليات خطوط الإنتاج.

الخطوات التالية