شروع عملیات خطوط لوله فایراستور

پیشینه

Pipeline Queries یک رابط پرس‌وجوی جدید برای Firestore است. این رابط، قابلیت‌های پرس‌وجوی پیشرفته‌ای از جمله عبارات پیچیده را ارائه می‌دهد. همچنین پشتیبانی از بسیاری از توابع جدید مانند min, max, substring, regex_match و array_contains_all را اضافه می‌کند. با Pipeline Queries، ایجاد شاخص نیز کاملاً اختیاری است و روند توسعه پرس‌وجوهای جدید را ساده می‌کند. Pipeline Queries همچنین بسیاری از محدودیت‌های شکل پرس‌وجو را حذف می‌کند و به شما امکان می‌دهد in بزرگ یا or پرس‌وجوهای بزرگ را مشخص کنید.

شروع به کار

برای نصب و مقداردهی اولیه SDK های کلاینت، به دستورالعمل‌های موجود در راهنمای شروع به کار مراجعه کنید.

نحو

بخش‌های زیر مروری بر سینتکس پرس‌وجوهای پایپ‌لاین (Pipeline Query) ارائه می‌دهند.

مفاهیم

یکی از تفاوت‌های قابل توجه در پرس‌وجوهای پایپ‌لاین، معرفی ترتیب صریح «مرحله‌ای» است. این امر بیان پرس‌وجوهای پیچیده‌تر را ممکن می‌سازد. با این حال، این یک انحراف قابل توجه از رابط پرس‌وجوی موجود است که در آن ترتیب مراحل ضمنی بود. مثال پرس‌وجوی پایپ‌لاین زیر را در نظر بگیرید:

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);
سویفت
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);
پایتون
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)
)

مقداردهی اولیه

پرس‌وجوهای خط لوله (Pipeline Queries) سینتکس بسیار آشنایی دارند که از پرس‌وجوهای موجود Cloud Firestore گرفته شده است. برای شروع، با نوشتن موارد زیر، یک پرس‌وجو را مقداردهی اولیه می‌کنید:

Web

const { getFirestore } = require("firebase/firestore");
const { execute } = require("firebase/firestore/pipelines");
const database = getFirestore(app, "enterprise");
const pipeline = database.pipeline();
سویفت
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();
پایتون
firestore_client = firestore.client(default_app, "your-new-enterprise-database")
pipeline = firestore_client.pipeline()

ساختار

چند اصطلاح وجود دارد که هنگام ایجاد پرس‌وجوهای پایپ‌لاین (Pipeline Query) باید آنها را درک کنید: مراحل (stages)، عبارات (expressions) و توابع (functions).

مثالی که مراحل و عبارات را در یک پرس و جو نشان می‌دهد

مراحل: یک خط لوله ممکن است از یک یا چند مرحله تشکیل شده باشد. از نظر منطقی، این مراحل نشان‌دهنده‌ی مجموعه‌ای از مراحل (یا مراحل) هستند که برای اجرای پرس‌وجو انجام می‌شوند. نکته: در عمل، مراحل ممکن است برای بهبود عملکرد، بدون ترتیب اجرا شوند. با این حال، این امر هدف یا صحت پرس‌وجو را تغییر نمی‌دهد.

عبارات: Stageها اغلب یک عبارت را می‌پذیرند که به شما امکان می‌دهد پرس‌وجوهای پیچیده‌تری را بیان کنید. عبارت ممکن است ساده باشد و از یک تابع واحد مانند eq("a", 1) تشکیل شده باشد. همچنین می‌توانید عبارات پیچیده‌تر را با تودرتو کردن عبارات مانند and(eq("a", 1), eq("b", 2)).

فیلد در مقابل ارجاعات ثابت

پرس‌وجوهای خط لوله از عبارات پیچیده پشتیبانی می‌کنند. به همین دلیل، ممکن است لازم باشد بین اینکه یک مقدار نشان دهنده یک فیلد است یا یک ثابت، تمایز قائل شویم. مثال زیر را در نظر بگیرید:

Web

const pipeline = db.pipeline()
  .collection("cities")
  .where(field("name").equal(constant("Toronto")));
سویفت
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")));
پایتون
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")
]));
سویفت
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();
پایتون
# 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)))
);
سویفت
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();
پایتون
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(...) همگی به شما امکان می‌دهند فیلدهایی را که از مرحله قبل برگردانده می‌شوند، تغییر دهید. این سه مرحله عموماً به عنوان مراحل projection-style شناخته می‌شوند.

توابع 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"))
);
سویفت
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();
پایتون
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")
  )
);
سویفت
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();
پایتون
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"))
);
سویفت
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();
پایتون
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 نیستید. در عوض، دو محدودیت اصلی وجود دارد که باید از آنها آگاه باشید:

  • مهلت: ۶۰ ثانیه (مشابه نسخه استاندارد).
  • میزان استفاده از حافظه: محدودیت ۱۲۸ مگابایت برای مقدار داده‌های قابل استفاده در طول اجرای کوئری.

خطاها

ممکن است به دلایل مختلفی با درخواست‌های ناموفق مواجه شوید. در اینجا لینکی به خطاهای رایج و اقدامات مرتبط با آن آمده است:

کد خطا اکشن
DEADLINE_EXCEEDED کوئری که اجرا می‌کنید از مهلت ۶۰ ثانیه‌ای فراتر رفته و نیاز به بهینه‌سازی بیشتری دارد. برای نکات به بخش عملکرد مراجعه کنید. اگر نمی‌توانید مشکل را ریشه‌یابی کنید، با تیم تماس بگیرید.
RESOURCE_EXHAUSTED کوئری که اجرا می‌کنید از محدودیت‌های حافظه فراتر رفته و نیاز به بهینه‌سازی بیشتری دارد. برای نکات به بخش عملکرد مراجعه کنید. اگر نمی‌توانید مشکل را ریشه‌یابی کنید، با تیم تماس بگیرید.
INTERNAL برای پشتیبانی با تیم تماس بگیرید .

عملکرد

برخلاف کوئری‌های موجود، کوئری‌های پایپ‌لاین نیازی به وجود همیشگی ایندکس ندارند. این بدان معناست که یک کوئری می‌تواند در مقایسه با کوئری‌های موجود که بلافاصله با خطای FAILED_PRECONDITION missing index با شکست مواجه می‌شدند، تأخیر بیشتری نشان دهد. برای بهبود عملکرد کوئری‌های پایپ‌لاین، می‌توانید چند مرحله را انجام دهید.

ایجاد ایندکس‌ها

شاخص مورد استفاده

توضیح پرس‌وجو به شما امکان می‌دهد تشخیص دهید که آیا پرس‌وجوی شما توسط یک شاخص ارائه می‌شود یا به عملیاتی با کارایی کمتر مانند اسکن جدول بازمی‌گردد. اگر پرس‌وجوی شما به طور کامل از یک شاخص ارائه نمی‌شود، می‌توانید با دنبال کردن دستورالعمل‌ها یک شاخص ایجاد کنید.

ایجاد شاخص‌ها

شما می‌توانید مستندات مدیریت ایندکس موجود را برای ایجاد ایندکس‌ها دنبال کنید. قبل از ایجاد یک ایندکس، با بهترین شیوه‌های کلی کار با ایندکس‌ها در 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())
);
سویفت
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();
پایتون
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"))
);
سویفت
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();
پایتون
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(...) می‌توانند برای اعمال این فیلترینگ سمت سرور استفاده شوند. این کار هم اندازه پاسخ (کاهش هزینه خروجی شبکه) و هم تأخیر را بهبود می‌بخشد.

ابزارهای عیب‌یابی

توضیح پرس و جو

توضیح پرس‌وجو به شما امکان می‌دهد تا معیارهای اجرا و جزئیات مربوط به شاخص‌های مورد استفاده را مشاهده کنید.

معیارها

پرس‌وجوهای خط لوله‌ای اگر کاملاً با معیارهای موجود Firestore یکپارچه شوند.

مشکلات/محدودیت‌های شناخته‌شده

شاخص‌های تخصصی

کوئری‌های پایپ‌لاین هنوز از انواع اندیس‌های array-contains و vector موجود پشتیبانی نمی‌کنند. فایراستور به جای رد کردن چنین کوئری‌هایی، تلاش خواهد کرد تا از سایر اندیس‌های 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)
);
سویفت
// 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());
پایتون
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 پشتیبانی نمی‌کند.

پشتیبانی آنلاین و آفلاین

کوئری‌های پایپ‌لاین هنوز قابلیت‌های بلادرنگ و آفلاین ندارند.

قدم بعدی چیست؟