בדף הזה מופיעות דוגמאות לאסטרטגיות של הוספה לאינדקס שאפשר להשתמש בהן בשאילתות עם מסנני טווח ואי-שוויון בכמה שדות, כדי ליצור חוויית שאילתה יעילה.
לפני שמבצעים אופטימיזציה של השאילתות, מומלץ לקרוא על המושגים שקשורים למסנני טווח ומסנני אי-שוויון בכמה נכסים .
אופטימיזציה של שאילתות באמצעות Query Explain
כדי לבדוק אם השאילתה והאינדקסים שבהם נעשה שימוש הם אופטימליים, אפשר ליצור שאילתה באמצעות Query Explain ולעיין בסיכום הביצוע.
Java
...
// Build the query
Query<Entity> query =
Query.newEntityQueryBuilder()
.setKind("employees")
.setFilter(
CompositeFilter.and(
PropertyFilter.gt("salary", 100000), PropertyFilter.gt("experience", 0)))
.setOrderBy(OrderBy("experience"), OrderBy("salary"))
.build();
// Set the explain options to get back *only* the plan summary
QueryResults<Entity> results = datastore.run(query, ExplainOptions.newBuilder().build());
// Get the explain metrics
Optional<ExplainMetrics> explainMetrics = results.getExplainMetrics();
if (!explainMetrics.isPresent()) {
throw new Exception("No explain metrics returned");
}
// Get the plan summary
PlanSummary planSummary = explainMetrics.get().getPlanSummary();
List<Map<String, Object>> indexesUsed = planSummary.getIndexesUsed();
System.out.println("----- Indexes Used -----");
indexesUsed.forEach(map -> map.forEach((s, o) -> System.out.println(s + ": " + o)));
// Get the execution stats
if (!explainMetrics.getExecutionStats().isPresent()) {
throw new Exception("No execution stats returned");
}
ExecutionStats queryStats = explainMetrics.getExecutionStats().get();
Map<String, Object> debugStats = queryStats.getDebugStats();
System.out.println("----- Debug Stats -----");
debugStats.forEach((s, o) -> System.out.println(s + ": " + o));
בדוגמה הבאה אפשר לראות איך שימוש בסדר הנכון של האינדקס חוסך את מספר הישויות ש-Firestore במצב Datastore סורק.
שאילתות פשוטות
בדוגמה הקודמת של אוסף עובדים, השאילתה הפשוטה שמופעלת עם האינדקס (salary, experience) היא:
GQL
SELECT *
FROM /employees
WHERE salary > 100000 AND experience > 0
ORDER BY experience, salary;
Java
Query<Entity> query =
Query.newEntityQueryBuilder()
.setKind("employees")
.setFilter(
CompositeFilter.and(
PropertyFilter.gt("salary", 100000), PropertyFilter.gt("experience", 0)))
.setOrderBy(OrderBy("experience"), OrderBy("salary"))
.build();
השאילתה סורקת 95,000 רשומות באינדקס רק כדי להחזיר 5 ישויות. מספר גדול של רשומות באינדקס נקראו אבל סוננו כי הן לא עמדו בתנאי השאילתה.
// Output query planning info { "indexesUsed": [ { "query_scope": "Collection Group", "properties": "(experience ASC, salary ASC, __name__ ASC)" } ] }, // Output Query Execution Stats { "resultsReturned": "5", "executionDuration": "2.5s", "readOperations": "100", "debugStats": { "index_entries_scanned": "95000", "documents_scanned": "5", "billing_details": { "documents_billable": "5", "index_entries_billable": "95000", "small_ops": "0", "min_query_cost": "0" } } }
כמו בדוגמה הקודמת, אפשר להסיק שההגבלה salary סלקטיבית יותר מההגבלה experience.
GQL
SELECT *
FROM /employees
WHERE salary > 100000 AND experience > 0
ORDER BY salary, experience;
Java
Query<Entity> query =
Query.newEntityQueryBuilder()
.setKind("employees")
.setFilter(
CompositeFilter.and(
PropertyFilter.gt("salary", 100000), PropertyFilter.gt("experience", 0)))
.setOrderBy(OrderBy("salary"), OrderBy("experience"))
.build();
כשמשתמשים באופן מפורש בסעיף orderBy() כדי להוסיף את התנאים בסדר הקודם, Firestore במצב Datastore משתמש באינדקס (salary, experience) כדי להריץ את השאילתה. מכיוון שהסינון לפי הטווח הראשון טוב יותר מהשאילתה הקודמת, השאילתה פועלת מהר יותר ומשתלמת יותר.
// Output query planning info { "indexesUsed": [ { "query_scope": "Collection Group", "properties": "(salary ASC, experience ASC, __name__ ASC)" } ], // Output Query Execution Stats "resultsReturned": "5", "executionDuration": "0.2s", "readOperations": "6", "debugStats": { "index_entries_scanned": "1000", "documents_scanned": "5", "billing_details": { "documents_billable": "5", "index_entries_billable": "1000", "small_ops": "0", "min_query_cost": "0" } } }