بدء استخدام عملية Firestore Pipelines

الخلفية

‫Pipeline Queries هي واجهة طلبات بحث جديدة في Firestore. توفّر هذه اللغة وظائف بحث متقدّمة، بما في ذلك عبارات معقّدة. ويضيف أيضًا دعمًا للعديد من الدوال الجديدة، مثل min, max, substring, regex_match وarray_contains_all. باستخدام "طلبات البحث عبر خطوط الإنتاج"، يصبح إنشاء الفهرس اختياريًا تمامًا، ما يؤدي إلى تبسيط عملية تطوير طلبات البحث الجديدة. تزيل "طلبات البحث في سلسلة المعالجة" أيضًا العديد من القيود المفروضة على شكل طلب البحث، ما يتيح لك تحديد طلبات بحث كبيرة 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()

البنية

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

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

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

التعبيرات: غالبًا ما تقبل المراحل تعبيرًا يتيح لك التعبير عن طلبات بحث أكثر تعقيدًا. قد تكون الصيغة بسيطة وتتألف من دالة واحدة مثل 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")))
)

المسارح

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

تمثّل مرحلة الإدخال المرحلة الأولى من طلب البحث. تحدّد هذه السمة المجموعة الأولية من المستندات التي تبحث فيها. بالنسبة إلى استعلامات مسار الإحالة الناجحة، يشبه ذلك إلى حد كبير الاستعلامات الحالية، حيث تبدأ معظم الاستعلامات بالمرحلة 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 التواصل مع الفريق للحصول على الدعم

الأداء

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

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

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

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

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

يمكنك اتّباع مستندات إدارة الفهرس الحالية لإنشاء الفهارس. قبل إنشاء فهرس، يُرجى الاطّلاع على أفضل الممارسات العامة المتعلّقة بالفهارس في 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 استخدام فهارس متفرقة وغير متفرقة. لمزيد من المعلومات، اطّلِع على كثافة الفهرسة.

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

يمكن أن تتخطّى 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 [...])، يمكنها تجنُّب جلب أي بيانات من المستندات الرئيسية نفسها. في هذه الحالة، لا يهم الترتيب في الفهرس، ويتم استخدام [...] للإشارة إلى ذلك.

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

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

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

شرح طلب البحث

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

المقاييس

تتكامل طلبات بحث Pipeline بالكامل مع مقاييس Firestore الحالية.

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

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

لا تتيح طلبات البحث في مسار البيانات بعد array-contains وvector أنواع الفهرس الحالية. بدلاً من رفض طلبات البحث هذه، ستحاول Firestore استخدام فهارس ascending وdescending أخرى حالية. من المتوقّع أن تكون طلبات البحث في Pipeline التي تتضمّن تعبيرات 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 بعد.

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

لا تتوفّر إمكانات الوقت الفعلي والتعديل بلا إنترنت في طلبات البحث في مسار العرض بعد.

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