רקע
פעולות בצינור עיבוד הנתונים מספקות ממשק שאילתות חדש ל-Cloud Firestore, שתומך בפונקציונליות מתקדמת של שאילתות ובביטויים מורכבים. הוא כולל הרבה פונקציות חדשות, כמו min(...), max(...), substring(...), regex_match(...) ו-array_contains_all(...), ושלבים לביצוע טרנספורמציות מורכבות.
תחילת העבודה
כדי להתקין ולהפעיל ערכות 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) )
המשך
pipeline := client.Pipeline(). Collection("cities"). Where(firestore.FieldOf("population").GreaterThan(100000)). Sort(firestore.Orders(firestore.Ascending(firestore.FieldOf("name")))). 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()
המשך
client, err := firestore.NewClientWithDatabase(ctx, projectID, databaseID) if err != nil { fmt.Fprintf(w, "firestore.NewClientWithDatabase failed: %v", err) return err } defer client.Close() pipeline := client.Pipeline().Collection("books")
מבנה
יש כמה מונחים שחשוב להבין כשיוצרים פעולות בצינור עיבוד נתונים: שלבים, ביטויים, פונקציות ו-subquery wrappers.

שלבים: צינור עיבוד נתונים יכול לכלול שלב אחד או יותר. מבחינה לוגית, הם מייצגים את סדרת השלבים (או השלבים) שבוצעו כדי להריץ את השאילתה.
ביטויים: שלבים בדרך כלל יקבלו ביטוי שמאפשר לכם לבטא שאילתות מורכבות יותר. הביטוי יכול להיות פשוט ולהכיל פונקציה אחת, כמו eq("a", 1). אפשר גם להשתמש בביטויים מורכבים יותר על ידי הטמעה של ביטויים כמו and(eq("a", 1), eq("b", 2)).
פונקציות wrapper של שאילתות משנה: פונקציות כמו array() ו-scalar() מאפשרות להטמיע צינור עיבוד נתונים בתור ביטוי בתוך שלב.
שדות / קבועים / משתנים
פעולות בצינור עיבוד הנתונים תומכות בביטויים מורכבים. לכן חשוב להבחין אם הערך מייצג שדה, קבוע או משתנה.
שדות מתייחסים לנתונים בתוך מסמכים, וקבועים מאפשרים לציין כל ערך כארגומנט לביטוי. לעומת זאת, משתנים מאפשרים להגדיר ערכים זמניים ולהשתמש בהם, והם מוגבלים להיקף של הרצת השאילתה ולא למסמכים שעוברים עיבוד. בהמשך מופיעה סקירה כללית של המושגים האלה. בשלב let(...) מוסבר איך לקרוא ולכתוב משתנים במהלך הרצת השאילתה.
| שדות | קבועים | משתנים | |
|---|---|---|---|
| מטרה | לגשת לשדות במסמכים או לאחסן אותם | לציין ערך קבוע | שימוש בערכים זמניים במהלך הפעלת צינור עיבוד הנתונים |
| שימוש ב-SDK | field("name") |
constant("val") |
variable("name") |
| היקף | מקומי למסמך הנוכחי | גלובלית | גלובלי לצינור עיבוד הנתונים ולצינורות המשנה של עיבוד הנתונים |
| הפניה לא מוגדרת | הערך שמתקבל הוא absent |
לא רלוונטי | יוצרת שגיאת זמן ריצה |
לדוגמה:
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 := client.Pipeline().Collection("cities"). Where(firestore.FieldOf("name").Equal(firestore.ConstantOf("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() )
המשך
// Return all restaurants in San Francisco results1, err := client.Pipeline().Collection("cities/sf/restaurants").Execute(ctx).Results().GetAll() if err != nil { fmt.Fprintf(w, "GetAll failed: %v", err) return err } // Return all restaurants results2, err := client.Pipeline().CollectionGroup("restaurants").Execute(ctx).Results().GetAll() if err != nil { fmt.Fprintf(w, "GetAll failed: %v", err) return err } // Return all documents across all collections in the database (the entire database) results3, err := client.Pipeline().Database().Execute(ctx).Results().GetAll() if err != nil { fmt.Fprintf(w, "GetAll failed: %v", err) return err } // Batch read of 3 documents results4, err := client.Pipeline(). Documents([]*firestore.DocumentRef{ client.Collection("cities").Doc("SF"), client.Collection("cities").Doc("DC"), client.Collection("cities").Doc("NY"), }). Execute(ctx).Results().GetAll() if err != nil { fmt.Fprintf(w, "GetAll failed: %v", err) return err }
כמו בכל השלבים האחרים, סדר התוצאות משלבי הקלט האלה לא יציב. אם נדרש סדר ספציפי, תמיד צריך להוסיף אופרטור 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() )
המשך
results1, err := client.Pipeline(). Collection("books"). Where(firestore.FieldOf("rating").Equal(5)). Where(firestore.FieldOf("published").LessThan(1900)). Execute(ctx).Results().GetAll() if err != nil { fmt.Fprintf(w, "GetAll failed: %v", err) return err } results2, err := client.Pipeline(). Collection("books"). Where(firestore.And( firestore.FieldOf("rating").Equal(5), firestore.FieldOf("published").LessThan(1900), )). Execute(ctx).Results().GetAll() if err != nil { fmt.Fprintf(w, "GetAll failed: %v", err) return err }
בחירה / הוספה והסרה של שדות
השלבים select(...), add_fields(...) ו-remove_fields(...) מאפשרים לשנות את השדות שמוחזרים משלב קודם. שלושת השלבים האלה נקראים בדרך כלל שלבים בסגנון הקרנה.
הפונקציות select(...) ו-add_fields(...) מאפשרות לציין את התוצאה של ביטוי לשם שדה שסופק על ידי המשתמש. השלב 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() )
המשך
snapshot := client.Pipeline(). Collection("books"). Aggregate( firestore.Accumulators(firestore.Average("rating").As("avg_rating")), firestore.WithAggregateGroups("genre"), ). Execute(ctx)
אם לא מציינים את 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() )
המשך
snapshot := client.Pipeline(). Collection("books"). Distinct(firestore.Fields( firestore.ToUpper(firestore.FieldOf("author")).As("author"), firestore.FieldOf("genre"), )). Execute(ctx)
פונקציות
פונקציות הן אבני הבניין ליצירת ביטויים ושאילתות מורכבות. רשימה מלאה של פונקציות עם דוגמאות מופיעה בחומר העזר בנושא פונקציות. תזכורת קצרה: זה המבנה של שאילתה אופיינית:

בשלבים רבים אפשר להשתמש בביטויים שמכילים פונקציה אחת או יותר. השימוש הנפוץ ביותר בפונקציות יהיה בשלבים 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() )
המשך
// Type 1: Scalar (for use in non-aggregation stages) // Example: Return the min store price for each book. results1, err := client.Pipeline(). Collection("books"). Select(firestore.Fields( firestore.LogicalMinimum(firestore.FieldOf("current"), firestore.FieldOf("updated")).As("price_min"), )). Execute(ctx).Results().GetAll() if err != nil { fmt.Fprintf(w, "GetAll failed: %v", err) return err } // Type 2: Aggregation (for use in aggregate stages) // Example: Return the min price of all books. results2, err := client.Pipeline(). Collection("books"). Aggregate(firestore.Accumulators( firestore.Minimum("price").As("min_price"), )). Execute(ctx).Results().GetAll() if err != nil { fmt.Fprintf(w, "GetAll failed: %v", err) return err }
מגבלות
ברוב המקרים, במהדורת Enterprise אין מגבלות על צורת השאילתה. במילים אחרות, אתם לא מוגבלים למספר קטן של ערכים בשאילתת IN או OR. במקום זאת, יש שתי מגבלות עיקריות שצריך להכיר:
- מועד אחרון: 60 שניות (זהה למהדורת Standard).
- שימוש בזיכרון: מגבלה של 128MiB על כמות הנתונים המהוונים במהלך הרצת השאילתה.
שגיאות
יכולות להיות כמה סיבות לכך ששאילתות נכשלות. בקישור הבא מפורטות שגיאות נפוצות והפעולות שאפשר לבצע כדי לפתור אותן:
| קוד שגיאה | פעולה |
DEADLINE_EXCEEDED
|
השאילתה שאתם מריצים חורגת מהמועד האחרון של 60 שניות ודורשת אופטימיזציה נוספת. טיפים בנושא מופיעים בקטע "ביצועים". אם אתם לא מצליחים לזהות את שורש הבעיה, פנו לצוות. |
RESOURCE_EXHAUSTED
|
השאילתה שאתם מריצים חורגת ממגבלות הזיכרון וצריך לבצע אופטימיזציה נוספת. טיפים בנושא מופיעים בקטע "ביצועים". אם אתם לא מצליחים לזהות את שורש הבעיה, פנו לצוות. |
INTERNAL
|
פנו לצוות התמיכה. |
ביצועים
במסדי נתונים במהדורת Enterprise לא נדרש אינדקס.
המשמעות היא ששאילתה יכולה להציג זמן אחזור גבוה יותר בהשוואה לשאילתות קיימות, שהיו נכשלות מיד עם שגיאת אינדקס חסר של FAILED_PRECONDITION. כדי לשפר את הביצועים של פעולות ב-Pipeline, אתם יכולים לבצע כמה פעולות.
יצירת אינדקסים
האינדקס שנעשה בו שימוש
הסבר השאילתה מאפשר לכם לזהות אם השאילתה מוגשת על ידי אינדקס או חוזרת לפעולה פחות יעילה, כמו סריקת טבלה. אם השאילתה לא מוצגת במלואה מאינדקס, אפשר ליצור אינדקס לפי ההוראות.
יצירת אינדקסים
כדי ליצור אינדקסים, אפשר לפעול לפי המסמכים הקיימים בנושא ניהול אינדקסים. לפני שיוצרים אינדקס, כדאי להכיר את השיטות המומלצות הכלליות לאינדקסים ב-Cloud Firestore. כדי לוודא שהשאילתה יכולה להשתמש באינדקסים, צריך לפעול לפי השיטות המומלצות ליצירת אינדקסים עם שדות בסדר הבא:
- כל השדות שישמשו במסנני שוויון (בכל סדר)
- כל השדות שלפיהם יתבצע המיון (באותו סדר)
- שדות שישמשו במסנני טווח או אי-שוויון בסדר יורד של סלקטיביות אילוצי השאילתה
לדוגמה, בשאילתה הבאה,
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() )
המשך
snapshot := client.Pipeline(). Collection("books"). Where(firestore.FieldOf("published").LessThan(1900)). Where(firestore.FieldOf("genre").Equal("Science Fiction")). Where(firestore.FieldOf("rating").GreaterThan(4.3)). Sort(firestore.Orders(firestore.Descending(firestore.FieldOf("published")))). Execute(ctx)
האינדקס המומלץ הוא אינדקס בהיקף של אוסף ב-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() )
המשך
snapshot := client.Pipeline(). Collection("books"). Where(firestore.FieldOf("category").Like("%fantasy%")). Where(firestore.FieldOf("title").FieldExists()). Where(firestore.FieldOf("author").FieldExists()). Select(firestore.Fields("title", "author")). Execute(ctx)
אם במסד הנתונים כבר יש אינדקס של היקף האוסף ב-books עבור (category [...], title [...], author [...]), הוא יכול להימנע מאחזור של כל דבר מהמסמכים הראשיים עצמם. במקרה כזה, הסדר באינדקס לא משנה, והערך [...] משמש לציון העובדה הזו.
הגבלת השדות שיוחזרו
כברירת מחדל, שאילתת Cloud Firestore מחזירה את כל השדות במסמך, בדומה ל-SELECT * במערכות יחסיות. עם זאת, אם האפליקציה צריכה רק קבוצת משנה של השדות, אפשר להשתמש בשלבים select(...) או restrict(...) כדי להעביר את הסינון הזה לצד השרת. כך אפשר להקטין את גודל התגובה (ולהפחית את עלות תעבורת הנתונים היוצאת ברשת) וגם לשפר את זמן האחזור.
כלים לפתרון בעיות
הסבר על השאילתה
התכונה Query Explain מאפשרת לכם לראות את מדדי הביצוע ופרטים על האינדקסים שבהם נעשה שימוש.
מדדים
פעולות בצינור עיבוד הנתונים אם הן משולבות באופן מלא עם מדדי Cloud Firestore קיימים.
בעיות מוכרות / מגבלות
אינדקסים מיוחדים
פעולות צינור עיבוד נתונים עדיין לא תומכות בסוגי אינדקסים קיימים של array-contains ו-vector. במקום לדחות שאילתות כאלה, Cloud Firestore ינסה להשתמש באינדקסים קיימים אחרים של ascending ו-descending. לכן, ביטויים עם array_contains או find_nearest יהיו איטיים יותר מהביטויים המקבילים הקיימים שלהם.
תמיכה בזמן אמת ובמצב אופליין
לפעולות בצינור עיבוד הנתונים אין יכולות של זמן אמת ואופליין.