תיאור
בשלב aggregate מחושבות תוצאות מצטברות (למשל, ספירה, סכום) מהמסמכים שהוחזרו בשלב הקודם.
אופציונלי: אם מציינים ביטוי לקיבוץ, המסמכים מקובצים על סמך הביטויים שצוינו, ואז מוחלות על כל קבוצה פונקציות מצטברות.
תחביר
בצבירות ללא group-by, השלב aggregate מקבל ביטוי צבירה אחד או יותר עם כינוי:
Node.js
const cities = await db.pipeline()
.collection("/cities")
.aggregate(
countAll().as("total"),
average("population").as("avg_population")
)
.execute();
בצבירות עם קיבוץ, נדרשות קבוצות נוספות מלבד הצבירות:
Node.js
const result = await db.pipeline()
.collectionGroup('citites')
.aggregate({
accumulators: [
countAll().as('cities'),
field('population').sum().as('total_popoluation')
],
groups: [field('location.state').as('state')]
})
.execute();
התנהגות
צבירת נתונים ללא קיבוץ
יוצרים אוסף cities עם המסמכים הבאים:
Node.js
await db.collection('cities').doc('SF').set({name: 'San Francisco', state: 'CA', country: 'USA', population: 870000});
await db.collection('cities').doc('LA').set({name: 'Los Angeles', state: 'CA', country: 'USA', population: 3970000});
await db.collection('cities').doc('NY').set({name: 'New York', state: 'NY', country: 'USA', population: 8530000});
await db.collection('cities').doc('TOR').set({name: 'Toronto', state: null, country: 'Canada', population: 2930000});
await db.collection('cities').doc('MEX').set({name: 'Mexico City', state: null, country: 'Mexico', population: 9200000});
כדי לגלות את המספר הכולל של הערים ואת גודל האוכלוסייה הממוצע שלהן:
Node.js
const cities = await db.pipeline()
.collection("/cities")
.aggregate(
countAll().as("total"),
average("population").as("avg_population")
)
.execute();
שמפיק את הפלט:
{avg_population: 5100000, total: 5}
ביצוע צבירות נתונים בקבוצות
אם מספקים ארגומנט groups, אפשר לבצע צבירה בכל קבוצה נפרדת.
לדוגמה, כדי למצוא את העיר עם האוכלוסייה הגדולה ביותר בכל מדינה ובכל מדינה בארה"ב:
Node.js
const cities = await db.pipeline()
.collection("/cities")
.aggregate({
accumulators: [
countAll().as("number_of_cities"),
maximum("population").as("max_population")
],
groups: ["country", "state"]
})
.execute();
שנותן:
{country: "USA", state: "CA", max_population: 3970000, number_of_cities: 2},
{country: "USA", state: "NY", max_population: 8530000, number_of_cities: 1},
{country: "Canada", state: null, max_population: 2930000, number_of_cities: 1},
{country: "Mexico", state: null, max_population: 9200000, number_of_cities: 1}
ביטויים מורכבים בקבוצות
בשלב aggregate אפשר לקבץ לא רק לפי ערכי שדות, אלא גם לפי תוצאות של ביטויים מורכבים. אפשר להשתמש בכל ביטוי שתקף בשלב select כמפתח קיבוץ. כך אפשר ליהנות מקיבוץ גמיש שמבוסס על ערכים או תנאים מחושבים.
לדוגמה, כדי לקבץ לפי הערך בשדה המדינה (אם הוא null) ולגלות את גודל האוכלוסייה בכל קבוצה:
Node.js
const cities = await db.pipeline()
.collection("/cities")
.aggregate({
accumulators: [
sum("population").as("total_population")
],
groups: [equal(field("state"), null).as("state_is_null")]
})
.execute();
יוחזר:
{state_is_null: true, total_population: 12130000}
{state_is_null: false, total_population: 13370000}
התנהגויות של אתרי אגרגטור
אפשר למצוא את התנהגות הצבירה של כל פונקציה נתמכת (למשל count, sum, avg) בדף הייעודי בנושא פונקציות צבירה.
התנהגויות מרכזיות בקבוצה
כשמקבצים מסמכים, Firestore משתמש בסמנטיקה של שוויון כדי לקבוע אם ערכים שייכים לאותה קבוצה.
המשמעות היא שערכים שווי ערך, למשל ערכים מספריים שווי ערך מבחינה מתמטית, מקובצים יחד, ללא קשר לסוג המקורי (מספר שלם של 32 ביט, מספר שלם של 64 ביט, מספרים עם נקודה עשרונית, decimal128 וכו').
לדוגמה, באוסף numerics עם מסמכים שונים שמכילים ערכים של מספר שלם בן 32 ביט 1, מספר שלם בן 64 ביט 1L ומספר נקודה צפה 1.0, כל הערכים יצטברו באותה קבוצה.foo הפעלת ספירה
עם קיבוץ לפי foo תחזיר:
{foo: 1.0, count: 3}
במקרים כאלה שבהם יש בערכת הנתונים ערכים שונים ששווים זה לזה, ערך הפלט של הקבוצה יכול להיות כל אחד מהערכים האלה.
בדוגמה הזו, foo יכול להיות 1, 1L או 1.0.
גם אם נראה שהבחירה היא דטרמיניסטית, לא מומלץ להסתמך על ההתנהגות של בחירת ערך ספציפי.
שימוש בזיכרון
אופן הביצוע של הצבירה תלוי באינדקסים הזמינים. אם אופטימיזציית השאילתות לא בוחרת אינדקס מתאים, צריך לשמור את כל הקבוצות בזיכרון.
אם יש מספר גדול מאוד של קבוצות, או אם כל קבוצה גדולה מאוד (למשל, קיבוץ לפי ערכים גדולים מאוד), יכול להיות שייגמר הזיכרון בשלב הזה.
במקרים כאלה, כדאי להחיל מסננים כדי להגביל את מערך הנתונים לצורך צבירה, לקבץ לפי שדות קטנים יותר או פחות שדות, או ליצור אינדקסים לפי ההמלצות כדי להימנע משימוש רב בזיכרון. התכונה Query Explain תספק מידע על תוכנית ההפעלה של השאילתה ועל נתוני הפרופיל כדי לעזור בניפוי הבאגים.