עבודה עם רשימות של נתונים באינטרנט

קבלת הפניה למסד נתונים

כדי לקרוא או לכתוב נתונים במסד הנתונים, צריך מופע של firebase.database.Reference:

Web

import { getDatabase } from "firebase/database";

const database = getDatabase();

Web

var database = firebase.database();

קריאה וכתיבה של רשימות

הוספה לרשימת נתונים

משתמשים בשיטה push() כדי לצרף נתונים לרשימה באפליקציות מרובות משתמשים. השיטה push() יוצרת מפתח ייחודי בכל פעם שמוסיפים צאצא חדש להפניה שצוינה ב-Firebase. באמצעות המפתחות שנוצרים באופן אוטומטי לכל רכיב חדש ברשימה, כמה לקוחות יכולים להוסיף רכיבי צאצא לאותו מיקום בו-זמנית בלי שיהיו התנגשויות בכתיבה. המפתח הייחודי שנוצר על ידי push() מבוסס על חותמת זמן, ולכן הפריטים ברשימה מסודרים אוטומטית בסדר כרונולוגי.

אפשר להשתמש בהפניה לנתונים החדשים שמוחזרים על ידי השיטה push() כדי לקבל את הערך של המפתח שנוצר אוטומטית עבור הצאצא או להגדיר נתונים עבור הצאצא. המאפיין .key של הפניה מסוג push() מכיל את המפתח שנוצר אוטומטית.

אפשר להשתמש במפתחות שנוצרו אוטומטית כדי לפשט את תהליך השטחת מבנה הנתונים. מידע נוסף זמין בדוגמה של פיצול נתונים.

לדוגמה, אפשר להשתמש ב-push() כדי להוסיף פוסט חדש לרשימת פוסטים באפליקציה של רשת חברתית:

Web

import { getDatabase, ref, push, set } from "firebase/database";

// Create a new post reference with an auto-generated id
const db = getDatabase();
const postListRef = ref(db, 'posts');
const newPostRef = push(postListRef);
set(newPostRef, {
    // ...
});

Web

// Create a new post reference with an auto-generated id
var postListRef = firebase.database().ref('posts');
var newPostRef = postListRef.push();
newPostRef.set({
    // ...
});

האזנה לאירועים משוכפלים

אירועים שקשורים לילדים מופעלים בתגובה לפעולות ספציפיות שמתרחשות לילדים של צומת מפעולה כמו הוספה של צומת צאצא חדש באמצעות השיטה push() או עדכון של צומת צאצא באמצעות השיטה update().

אירוע שימוש אופייני
child_added אחזור רשימות של פריטים או האזנה להוספות לרשימה של פריטים. האירוע הזה מופעל פעם אחת לכל צאצא קיים, ואז שוב בכל פעם שמוסיפים צאצא חדש לנתיב שצוין. המאזין מקבל תמונת מצב שמכילה את הנתונים של צאצא חדש.
child_changed חיפוש שינויים בפריטים ברשימה. האירוע הזה מופעל בכל פעם שמשנים צומת צאצא. השינויים האלה כוללים שינויים בצאצאים של צומת הילד. תמונת המצב שמועברת למאזין האירועים מכילה את הנתונים המעודכנים של הרכיב הצאצא.
child_removed האזנה להסרה של פריטים מרשימה. האירוע הזה מופעל כשמסירים צאצא מיידי.קובץ ה-snapshot שמועבר לבלוק הקריאה החוזרת מכיל את הנתונים של הצאצא שהוסר.
child_moved האזנה לשינויים בסדר הפריטים ברשימה ממוספרת. האירועים מסוג child_moved תמיד מופיעים אחרי האירוע מסוג child_changed שגרם לשינוי בסדר הפריטים (על סמך שיטת המיון הנוכחית).

כל אחת מהאפשרויות האלה יכולה להיות שימושית להאזנה לשינויים בצומת ספציפי במסד נתונים. לדוגמה, אפליקציה לבלוגים ברשתות חברתיות יכולה להשתמש בשיטות האלה יחד כדי לעקוב אחרי הפעילות בתגובות של פוסט, כמו שמוצג בהמשך:

Web

import { getDatabase, ref, onChildAdded, onChildChanged, onChildRemoved } from "firebase/database";

const db = getDatabase();
const commentsRef = ref(db, 'post-comments/' + postId);
onChildAdded(commentsRef, (data) => {
  addCommentElement(postElement, data.key, data.val().text, data.val().author);
});

onChildChanged(commentsRef, (data) => {
  setCommentValues(postElement, data.key, data.val().text, data.val().author);
});

onChildRemoved(commentsRef, (data) => {
  deleteComment(postElement, data.key);
});

Web

var commentsRef = firebase.database().ref('post-comments/' + postId);
commentsRef.on('child_added', (data) => {
  addCommentElement(postElement, data.key, data.val().text, data.val().author);
});

commentsRef.on('child_changed', (data) => {
  setCommentValues(postElement, data.key, data.val().text, data.val().author);
});

commentsRef.on('child_removed', (data) => {
  deleteComment(postElement, data.key);
});

האזנה לאירועי ערך

הדרך המומלצת לקרוא רשימות של נתונים היא להאזין לאירועי צאצא, אבל יש מצבים שבהם כדאי להאזין לאירועי ערך בהפניה לרשימה.

צירוף של value observer לרשימת נתונים יחזיר את כל רשימת הנתונים כתמונת מצב יחידה, שאפשר להשתמש בלולאה כדי לגשת לכל אחד מהפריטים שברשימה.

גם אם יש רק התאמה אחת לשאילתה, התמונה היא עדיין רשימה, רק שהיא מכילה פריט אחד. כדי לגשת לפריט, צריך להשתמש בלולאה כדי לעבור על התוצאה:

Web

import { getDatabase, ref, onValue } from "firebase/database";

const db = getDatabase();
const dbRef = ref(db, '/a/b/c');

onValue(dbRef, (snapshot) => {
  snapshot.forEach((childSnapshot) => {
    const childKey = childSnapshot.key;
    const childData = childSnapshot.val();
    // ...
  });
}, {
  onlyOnce: true
});

Web

ref.once('value', (snapshot) => {
  snapshot.forEach((childSnapshot) => {
    var childKey = childSnapshot.key;
    var childData = childSnapshot.val();
    // ...
  });
});

הדפוס הזה יכול להיות שימושי כשרוצים לאחזר את כל הצאצאים של רשימה בפעולה אחת, במקום להאזין לאירועים נוספים של הוספת צאצא.

מיון וסינון של נתונים

אפשר להשתמש במחלקה Realtime Database Query כדי לאחזר נתונים ממוינים לפי מפתח, לפי ערך או לפי ערך של צאצא. אפשר גם לסנן את התוצאה הממוינת לפי מספר מסוים של תוצאות או לפי טווח של מפתחות או ערכים.

מיון נתונים

כדי לאחזר נתונים ממוינים, צריך קודם לציין אחת משיטות המיון כדי לקבוע את סדר התוצאות:

שיטה שימוש
orderByChild() מיון התוצאות לפי הערך של מפתח צאצא ספציפי או נתיב צאצא מוטמע.
orderByKey() מיון התוצאות לפי מפתחות צאצא.
orderByValue() מיון התוצאות לפי ערכי הצאצא.

אפשר להשתמש רק בשיטה אחת של מיון בכל פעם. הפעלת שיטה של מיון לפי סדר (order-by) מספר פעמים באותה שאילתה גורמת לשגיאה.

בדוגמה הבאה מוצג איך אפשר לאחזר רשימה של הפוסטים המובילים של משתמש, ממוינים לפי מספר הכוכבים:

Web

import { getDatabase, ref, query, orderByChild } from "firebase/database";
import { getAuth } from "firebase/auth";

const db = getDatabase();
const auth = getAuth();

const myUserId = auth.currentUser.uid;
const topUserPostsRef = query(ref(db, 'user-posts/' + myUserId), orderByChild('starCount'));

Web

var myUserId = firebase.auth().currentUser.uid;
var topUserPostsRef = firebase.database().ref('user-posts/' + myUserId).orderByChild('starCount');

השאילתה הזו מגדירה שאילתה, שכאשר משלבים אותה עם listener צאצא, מסנכרנת את הלקוח עם הפוסטים של המשתמש מהנתיב במסד הנתונים על סמך מזהה המשתמש, ומסדרת אותם לפי מספר הכוכבים שכל פוסט קיבל. הטכניקה הזו של שימוש במזהים כמפתחות אינדקס נקראת פיצול נתונים. אפשר לקרוא עליה עוד במאמר Structure Your Database (ארגון מסד הנתונים).

הקריאה ל-method‏ orderByChild() מציינת את מפתח הילד כדי להזמין את התוצאות. במקרה כזה, הפוסטים ממוינים לפי הערך של פריט הצאצא "starCount" המתאים. אפשר גם להזמין שאילתות לפי צאצאים מוטמעים, אם יש לכם נתונים שנראים כך:

"posts": {
  "ts-functions": {
    "metrics": {
      "views" : 1200000,
      "likes" : 251000,
      "shares": 1200,
    },
    "title" : "Why you should use TypeScript for writing Cloud Functions",
    "author": "Doug",
  },
  "android-arch-3": {
    "metrics": {
      "views" : 900000,
      "likes" : 117000,
      "shares": 144,
    },
    "title" : "Using Android Architecture Components with Firebase Realtime Database (Part 3)",
    "author": "Doug",
  }
},

במקרה כזה, אפשר להזמין את רכיבי הרשימה לפי ערכים שמוטמעים מתחת למפתח metrics על ידי ציון הנתיב היחסי לצאצא המוטמע בקריאה metrics.orderByChild()

Web

import { getDatabase, ref, query, orderByChild } from "firebase/database";

const db = getDatabase();
const mostViewedPosts = query(ref(db, 'posts'), orderByChild('metrics/views'));

Web

var mostViewedPosts = firebase.database().ref('posts').orderByChild('metrics/views');

מידע נוסף על הסדר של סוגי נתונים אחרים זמין במאמר איך מסודרים נתוני שאילתות.

סינון נתונים

כדי לסנן נתונים, אפשר לשלב כל אחת מהשיטות של הגבלה או טווח עם שיטת מיון כשיוצרים שאילתה.

שיטה שימוש
limitToFirst() מגדיר את המספר המקסימלי של פריטים שיוחזרו מתחילת רשימת התוצאות הממוינת.
limitToLast() מגדיר את המספר המקסימלי של פריטים שיוחזרו מסוף הרשימה הממוינת של התוצאות.
startAt() הפונקציה מחזירה פריטים שגדולים מהמפתח או מהערך שצוינו או שווים להם, בהתאם לשיטת המיון שנבחרה.
startAfter() החזרת פריטים שגדולים מהמפתח או מהערך שצוינו בהתאם לשיטת המיון שנבחרה.
endAt() הפונקציה מחזירה פריטים שקטנים מהמפתח או מהערך שצוינו או שווים להם, בהתאם לשיטת המיון שנבחרה.
endBefore() החזרת פריטים שקטנים מהמפתח או מהערך שצוינו בהתאם לשיטת המיון שנבחרה.
equalTo() הפונקציה מחזירה פריטים ששווים למפתח או לערך שצוינו, בהתאם לשיטת המיון שנבחרה.

בניגוד לשיטות order-by, אפשר לשלב כמה פונקציות limit או range. לדוגמה, אפשר לשלב בין השיטות startAt() ו-endAt() כדי להגביל את התוצאות לטווח ערכים מסוים.

הגבלת מספר התוצאות

אפשר להשתמש בשיטות limitToFirst() ו-limitToLast() כדי להגדיר מספר מקסימלי של צאצאים שיסונכרנו לאירוע נתון. לדוגמה, אם משתמשים ב-limitToFirst() כדי להגדיר מגבלה של 100, בהתחלה מקבלים רק עד 100 אירועים מסוג child_added. אם יש לכם פחות מ-100 פריטים שמאוחסנים במסד הנתונים של Firebase, מופעל אירוע child_added לכל פריט.

כשהפריטים משתנים, אתם מקבלים אירועי child_added לגבי פריטים שנכללים בשאילתה ואירועי child_removed לגבי פריטים שמוסרים ממנה, כך שהמספר הכולל נשאר 100.

בדוגמה הבאה מוצג איך אפליקציה לדוגמה לניהול בלוג מגדירה שאילתה כדי לאחזר רשימה של 100 הפוסטים האחרונים של כל המשתמשים:

Web

import { getDatabase, ref, query, limitToLast } from "firebase/database";

const db = getDatabase();
const recentPostsRef = query(ref(db, 'posts'), limitToLast(100));

Web

var recentPostsRef = firebase.database().ref('posts').limitToLast(100);

בדוגמה הזו מוגדרת רק שאילתה. כדי לסנכרן נתונים בפועל, צריך לצרף אליה מאזין.

סינון לפי מפתח או ערך

אפשר להשתמש ב-startAt(),‏ startAfter(),‏ endAt(),‏ endBefore() ו-equalTo() כדי לבחור נקודות שרירותיות להתחלה, לסיום ולשוויון של שאילתות. המאפיין הזה יכול להיות שימושי כשמבצעים חלוקה לעמודים של נתונים או כשמחפשים פריטים עם פריטי צאצא עם ערך ספציפי.

איך נתוני השאילתות מסודרים

בקטע הזה מוסבר איך הנתונים ממוינים לפי כל אחת מהשיטות של order-by במחלקה Query.

orderByChild

כשמשתמשים ב-orderByChild(), נתונים שמכילים את מפתח הצאצא שצוין מסודרים באופן הבא:

  1. ילדים עם ערך null למפתח הילד שצוין מופיעים ראשונים.
  2. אחריהם מופיעים רכיבי צאצא עם הערך false עבור מפתח הצאצא שצוין. אם לכמה צאצאים יש ערך של false, הם ממוינים לקסיקוגרפית לפי מפתח.
  3. אחריהם מופיעים רכיבי צאצא עם הערך true עבור מפתח הצאצא שצוין. אם לכמה צאצאים יש ערך של true, הם ממוינים לפי מפתח בסדר לקסיקוגרפי.
  4. אחריהם מופיעים ילדים עם ערך מספרי, ממוינים בסדר עולה. אם לכמה צאצאים יש את אותו ערך מספרי בצומת הצאצא שצוין, הם ממוינים לפי מפתח.
  5. מחרוזות מופיעות אחרי מספרים וממוינות בסדר עולה לפי סדר מילוני. אם לכמה צאצאים יש את אותו ערך בצומת הצאצא שצוין, הם מסודרים לפי מפתח בסדר לקסיקוגרפי.
  6. האובייקטים מופיעים בסוף וממוינים בסדר עולה לפי מפתח, בסדר לקסיקוגרפי.

orderByKey

כשמשתמשים ב-orderByKey() כדי למיין את הנתונים, הנתונים מוחזרים בסדר עולה לפי מפתח.

  1. הילדים עם מפתח שאפשר לנתח כמספר שלם בן 32 ביט מופיעים ראשונים, בסדר עולה.
  2. אחריהם מופיעים ילדים עם ערך מחרוזת כמפתח, ממוינים בסדר עולה לפי סדר מילוני.

orderByValue

כשמשתמשים ב-orderByValue(), הילדים מסודרים לפי הערך שלהם. קריטריוני המיון זהים לאלה שמופיעים ב-orderByChild(), אלא שהערך של הצומת משמש במקום הערך של מפתח צאצא שצוין.

ניתוק של listeners

כדי להסיר קריאות חוזרות, קוראים לשיטה off() בהפניה למסד הנתונים של Firebase.

אפשר להסיר מאזין יחיד על ידי העברה שלו כפרמטר אל off(). התקשרות אל off() במיקום ללא ארגומנטים מסירה את כל המאזינים במיקום הזה.

התקשרות אל off() במאזין הורה לא מסירה באופן אוטומטי מאזינים שרשומים בצמתי הצאצא שלו. צריך גם להתקשר אל off() בכל מאזין צאצא כדי להסיר את הקריאה החוזרת.

השלבים הבאים