משחזר נתונים

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

  1. מאזינים אסינכרוניים: נתונים המאוחסנים במסד נתונים בזמן אמת של Firebase מאוחזרים על ידי צירוף מאזין אסינכרוני להפניה למסד נתונים. המאזין מופעל פעם אחת עבור המצב ההתחלתי של הנתונים ושוב בכל פעם שהנתונים משתנים. מאזין אירועים עשוי לקבל מספר סוגים שונים של אירועים . מצב זה של אחזור נתונים נתמך ב-Java, Node.js ו- Python Admin SDK.
  2. החסימה כתובה: נתונים המאוחסנים במסד נתונים בזמן אמת של Firebase מאוחזרים על ידי הפעלת שיטת חסימה בהפניה למסד נתונים, אשר מחזירה את הנתונים המאוחסנים בהפניה. כל קריאת שיטה היא פעולה חד פעמית. זה אומר שה-SDK לא רושם כל התקשרות חוזרת שמאזינה לעדכוני נתונים הבאים. מודל זה של אחזור נתונים נתמך ב- Python ו- Go Admin SDK.

מתחילים

בואו נסתכל שוב על דוגמה לבלוגים מהמאמר הקודם כדי להבין כיצד לקרוא נתונים ממסד נתונים של Firebase. זכור שהפוסטים בבלוג באפליקציה לדוגמה מאוחסנים בכתובת האתר של מסד הנתונים https://docs-examples.firebaseio.com/server/saving-data/fireblog/posts.json . כדי לקרוא את נתוני הפוסטים שלך, אתה יכול לעשות את הפעולות הבאות:

Java
public static class Post {

  public String author;
  public String title;

  public Post(String author, String title) {
    // ...
  }

}

// Get a reference to our posts
final FirebaseDatabase database = FirebaseDatabase.getInstance();
DatabaseReference ref = database.getReference("server/saving-data/fireblog/posts");

// Attach a listener to read the data at our posts reference
ref.addValueEventListener(new ValueEventListener() {
  @Override
  public void onDataChange(DataSnapshot dataSnapshot) {
    Post post = dataSnapshot.getValue(Post.class);
    System.out.println(post);
  }

  @Override
  public void onCancelled(DatabaseError databaseError) {
    System.out.println("The read failed: " + databaseError.getCode());
  }
});
Node.js
// Get a database reference to our posts
const db = getDatabase();
const ref = db.ref('server/saving-data/fireblog/posts');

// Attach an asynchronous callback to read the data at our posts reference
ref.on('value', (snapshot) => {
  console.log(snapshot.val());
}, (errorObject) => {
  console.log('The read failed: ' + errorObject.name);
}); 
פִּיתוֹן
# Import database module.
from firebase_admin import db

# Get a database reference to our posts
ref = db.reference('server/saving-data/fireblog/posts')

# Read the data at the posts reference (this is a blocking operation)
print(ref.get())
ללכת

// Post is a json-serializable type.
type Post struct {
	Author string `json:"author,omitempty"`
	Title  string `json:"title,omitempty"`
}

// Create a database client from App.
client, err := app.Database(ctx)
if err != nil {
	log.Fatalln("Error initializing database client:", err)
}

// Get a database reference to our posts
ref := client.NewRef("server/saving-data/fireblog/posts")

// Read the data at the posts reference (this is a blocking operation)
var post Post
if err := ref.Get(ctx, &post); err != nil {
	log.Fatalln("Error reading value:", err)
}

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

ב-Java וב-Node.js, פונקציית ה-callback מקבלת DataSnapshot , שהיא תמונת מצב של הנתונים. תמונת מצב היא תמונה של הנתונים בהפניה מסוימת למסד נתונים בנקודת זמן אחת. קריאה val() / getValue() בתמונת מצב מחזירה את ייצוג האובייקט הספציפי לשפה של הנתונים. אם לא קיימים נתונים במיקום ההפניה, הערך של תמונת המצב הוא null . השיטה get() ב-Python מחזירה ייצוג Python של הנתונים ישירות. הפונקציה Get() ב-Go משחררת את הנתונים למבנה נתונים נתון.

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

קרא סוגי אירועים ב-Java ו-Node.js

ערך

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

ילד נוסף

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

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

Java
ref.addChildEventListener(new ChildEventListener() {
  @Override
  public void onChildAdded(DataSnapshot dataSnapshot, String prevChildKey) {
    Post newPost = dataSnapshot.getValue(Post.class);
    System.out.println("Author: " + newPost.author);
    System.out.println("Title: " + newPost.title);
    System.out.println("Previous Post ID: " + prevChildKey);
  }

  @Override
  public void onChildChanged(DataSnapshot dataSnapshot, String prevChildKey) {}

  @Override
  public void onChildRemoved(DataSnapshot dataSnapshot) {}

  @Override
  public void onChildMoved(DataSnapshot dataSnapshot, String prevChildKey) {}

  @Override
  public void onCancelled(DatabaseError databaseError) {}
});
Node.js
// Retrieve new posts as they are added to our database
ref.on('child_added', (snapshot, prevChildKey) => {
  const newPost = snapshot.val();
  console.log('Author: ' + newPost.author);
  console.log('Title: ' + newPost.title);
  console.log('Previous Post ID: ' + prevChildKey);
});

בדוגמה זו תמונת המצב תכיל אובייקט עם פוסט בודד בבלוג. מכיוון שה-SDK ממיר פוסטים לאובייקטים על ידי אחזור הערך, יש לך גישה למאפייני המחבר והכותרת של הפוסט על ידי קריאה author title בהתאמה. יש לך גם גישה למזהה הפוסט הקודם מהארגומנט השני prevChildKey .

הילד השתנה

האירוע child_changed מופעל בכל פעם שצומת צאצא משתנה. זה כולל שינויים כלשהם בצאצאים של צומת הילד. הוא משמש בדרך כלל בשילוב עם child_added ו- child_removed כדי להגיב לשינויים ברשימת פריטים. תמונת המצב שהועברה להתקשרות חוזרת של האירוע מכילה את הנתונים המעודכנים עבור הילד.

אתה יכול להשתמש ב- child_changed כדי לקרוא נתונים מעודכנים בפוסטים בבלוג כשהם נערכים:

Java
ref.addChildEventListener(new ChildEventListener() {
  @Override
  public void onChildAdded(DataSnapshot dataSnapshot, String prevChildKey) {}

  @Override
  public void onChildChanged(DataSnapshot dataSnapshot, String prevChildKey) {
    Post changedPost = dataSnapshot.getValue(Post.class);
    System.out.println("The updated post title is: " + changedPost.title);
  }

  @Override
  public void onChildRemoved(DataSnapshot dataSnapshot) {}

  @Override
  public void onChildMoved(DataSnapshot dataSnapshot, String prevChildKey) {}

  @Override
  public void onCancelled(DatabaseError databaseError) {}
});
Node.js
// Get the data on a post that has changed
ref.on('child_changed', (snapshot) => {
  const changedPost = snapshot.val();
  console.log('The updated post title is ' + changedPost.title);
});

הילד הוסר

האירוע child_removed מופעל כאשר ילד מיידי מוסר. הוא משמש בדרך כלל בשילוב עם child_added ו- child_changed . תמונת המצב שהועברה להתקשרות חוזרת של האירוע מכילה את הנתונים של הילד שהוסר.

בדוגמה של הבלוג, אתה יכול להשתמש ב- child_removed כדי לרשום הודעה על הפוסט שנמחק למסוף:

Java
ref.addChildEventListener(new ChildEventListener() {
  @Override
  public void onChildAdded(DataSnapshot dataSnapshot, String prevChildKey) {}

  @Override
  public void onChildChanged(DataSnapshot dataSnapshot, String prevChildKey) {}

  @Override
  public void onChildRemoved(DataSnapshot dataSnapshot) {
    Post removedPost = dataSnapshot.getValue(Post.class);
    System.out.println("The blog post titled " + removedPost.title + " has been deleted");
  }

  @Override
  public void onChildMoved(DataSnapshot dataSnapshot, String prevChildKey) {}

  @Override
  public void onCancelled(DatabaseError databaseError) {}
});
Node.js
// Get a reference to our posts
const ref = db.ref('server/saving-data/fireblog/posts');

// Get the data on a post that has been removed
ref.on('child_removed', (snapshot) => {
  const deletedPost = snapshot.val();
  console.log('The blog post titled \'' + deletedPost.title + '\' has been deleted');
});

ילד עבר

אירוע child_moved משמש בעת עבודה עם נתונים מסודרים, אשר מכוסה בסעיף הבא .

ערבויות לאירועים

מסד הנתונים של Firebase נותן כמה ערבויות חשובות לגבי אירועים:

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

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

Java
final AtomicInteger count = new AtomicInteger();

ref.addChildEventListener(new ChildEventListener() {
  @Override
  public void onChildAdded(DataSnapshot dataSnapshot, String prevChildKey) {
    // New child added, increment count
    int newCount = count.incrementAndGet();
    System.out.println("Added " + dataSnapshot.getKey() + ", count is " + newCount);
  }

  // ...
});

// The number of children will always be equal to 'count' since the value of
// the dataSnapshot here will include every child_added event triggered before this point.
ref.addListenerForSingleValueEvent(new ValueEventListener() {
  @Override
  public void onDataChange(DataSnapshot dataSnapshot) {
    long numChildren = dataSnapshot.getChildrenCount();
    System.out.println(count.get() + " == " + numChildren);
  }

  @Override
  public void onCancelled(DatabaseError databaseError) {}
});
Node.js
let count = 0;

ref.on('child_added', (snap) => {
  count++;
  console.log('added:', snap.key);
});

// length will always equal count, since snap.val() will include every child_added event
// triggered before this point
ref.once('value', (snap) => {
  console.log('initial data loaded!', snap.numChildren() === count);
});

ניתוק התקשרויות חוזרות

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

Java
// Create and attach listener
ValueEventListener listener = new ValueEventListener() {
    // ...
};
ref.addValueEventListener(listener);

// Remove listener
ref.removeEventListener(listener);
Node.js
ref.off('value', originalCallback);

אם העברתם הקשר של היקף ל- on() , יש להעביר אותו בעת ניתוק ההתקשרות חזרה:

Java
// Not applicable for Java
Node.js
ref.off('value', originalCallback, ctx);

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

Java
// No Java equivalent, listeners must be removed individually.
Node.js
// Remove all value callbacks
ref.off('value');

// Remove all callbacks of any type
ref.off();

קריאת נתונים פעם אחת

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

Java
ref.addListenerForSingleValueEvent(new ValueEventListener() {
  @Override
  public void onDataChange(DataSnapshot dataSnapshot) {
    // ...
  }

  @Override
  public void onCancelled(DatabaseError databaseError) {
    // ...
  }
});
Node.js
ref.once('value', (data) => {
  // do some stuff once
});
פִּיתוֹן
# Import database module.
from firebase_admin import db

# Get a database reference to our posts
ref = db.reference('server/saving-data/fireblog/posts')

# Read the data at the posts reference (this is a blocking operation)
print(ref.get())
ללכת
// Create a database client from App.
client, err := app.Database(ctx)
if err != nil {
	log.Fatalln("Error initializing database client:", err)
}

// Get a database reference to our posts
ref := client.NewRef("server/saving-data/fireblog/posts")

// Read the data at the posts reference (this is a blocking operation)
var post Post
if err := ref.Get(ctx, &post); err != nil {
	log.Fatalln("Error reading value:", err)
}

שאילתת נתונים

עם שאילתות מסד נתונים של Firebase, אתה יכול לאחזר נתונים באופן סלקטיבי על סמך גורמים שונים. כדי לבנות שאילתה במסד הנתונים שלך, אתה מתחיל בציון כיצד אתה רוצה שהנתונים שלך יהיו מסודרים באמצעות אחת מפונקציות ההזמנה: orderByChild() , orderByKey() , או orderByValue() . לאחר מכן תוכל לשלב את אלה עם חמש שיטות אחרות לביצוע שאילתות מורכבות: limitToFirst() , limitToLast() , startAt() , endAt() ו- equalTo() .

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

{
  "lambeosaurus": {
    "height" : 2.1,
    "length" : 12.5,
    "weight": 5000
  },
  "stegosaurus": {
    "height" : 4,
    "length" : 9,
    "weight" : 2500
  }
}

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

הזמנה לפי מפתח צאצא מוגדר

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

Java
public static class Dinosaur {

  public int height;
  public int weight;

  public Dinosaur(int height, int weight) {
    // ...
  }

}

final DatabaseReference dinosaursRef = database.getReference("dinosaurs");
dinosaursRef.orderByChild("height").addChildEventListener(new ChildEventListener() {
  @Override
  public void onChildAdded(DataSnapshot dataSnapshot, String prevChildKey) {
    Dinosaur dinosaur = dataSnapshot.getValue(Dinosaur.class);
    System.out.println(dataSnapshot.getKey() + " was " + dinosaur.height + " meters tall.");
  }

  // ...
});
Node.js
const ref = db.ref('dinosaurs');

ref.orderByChild('height').on('child_added', (snapshot) => {
  console.log(snapshot.key + ' was ' + snapshot.val().height + ' meters tall');
});
פִּיתוֹן
ref = db.reference('dinosaurs')
snapshot = ref.order_by_child('height').get()
for key, val in snapshot.items():
    print('{0} was {1} meters tall'.format(key, val))
ללכת

// Dinosaur is a json-serializable type.
type Dinosaur struct {
	Height int `json:"height"`
	Width  int `json:"width"`
}

ref := client.NewRef("dinosaurs")

results, err := ref.OrderByChild("height").GetOrdered(ctx)
if err != nil {
	log.Fatalln("Error querying database:", err)
}
for _, r := range results {
	var d Dinosaur
	if err := r.Unmarshal(&d); err != nil {
		log.Fatalln("Error unmarshaling result:", err)
	}
	fmt.Printf("%s was %d meteres tall", r.Key(), d.Height)
}

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

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

{
  "lambeosaurus": {
    "dimensions": {
      "height" : 2.1,
      "length" : 12.5,
      "weight": 5000
    }
  },
  "stegosaurus": {
    "dimensions": {
      "height" : 4,
      "length" : 9,
      "weight" : 2500
    }
  }
}

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

Java
dinosaursRef.orderByChild("dimensions/height").addChildEventListener(new ChildEventListener() {
  @Override
  public void onChildAdded(DataSnapshot dataSnapshot, String prevChildKey) {
    // ...
  }

  // ...
});
Node.js
const ref = db.ref('dinosaurs');
ref.orderByChild('dimensions/height').on('child_added', (snapshot) => {
  console.log(snapshot.key + ' was ' + snapshot.val().height + ' meters tall');
});
פִּיתוֹן
ref = db.reference('dinosaurs')
snapshot = ref.order_by_child('dimensions/height').get()
for key, val in snapshot.items():
    print('{0} was {1} meters tall'.format(key, val))
ללכת
ref := client.NewRef("dinosaurs")

results, err := ref.OrderByChild("dimensions/height").GetOrdered(ctx)
if err != nil {
	log.Fatalln("Error querying database:", err)
}
for _, r := range results {
	var d Dinosaur
	if err := r.Unmarshal(&d); err != nil {
		log.Fatalln("Error unmarshaling result:", err)
	}
	fmt.Printf("%s was %d meteres tall", r.Key(), d.Height)
}

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

הזמנה לפי מפתח

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

Java
dinosaursRef.orderByKey().addChildEventListener(new ChildEventListener() {
  @Override
  public void onChildAdded(DataSnapshot dataSnapshot, String prevChildKey) {
    System.out.println(dataSnapshot.getKey());
  }

  // ...
});
Node.js
var ref = db.ref('dinosaurs');
ref.orderByKey().on('child_added', (snapshot) => {
  console.log(snapshot.key);
});
פִּיתוֹן
ref = db.reference('dinosaurs')
snapshot = ref.order_by_key().get()
print(snapshot)
ללכת
ref := client.NewRef("dinosaurs")

results, err := ref.OrderByKey().GetOrdered(ctx)
if err != nil {
	log.Fatalln("Error querying database:", err)
}
snapshot := make([]Dinosaur, len(results))
for i, r := range results {
	var d Dinosaur
	if err := r.Unmarshal(&d); err != nil {
		log.Fatalln("Error unmarshaling result:", err)
	}
	snapshot[i] = d
}
fmt.Println(snapshot)

הזמנה לפי ערך

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

{
  "scores": {
    "bruhathkayosaurus" : 55,
    "lambeosaurus" : 21,
    "linhenykus" : 80,
    "pterodactyl" : 93,
    "stegosaurus" : 5,
    "triceratops" : 22
  }
}

כדי למיין את הדינוזאורים לפי הציון שלהם, תוכל לבנות את השאילתה הבאה:

Java
DatabaseReference scoresRef = database.getReference("scores");
scoresRef.orderByValue().addChildEventListener(new ChildEventListener() {
  @Override
  public void onChildAdded(DataSnapshot dataSnapshot, String prevChildKey) {
    System.out.println("The " + dataSnapshot.getKey() + " score is " + dataSnapshot.getValue());
  }

  // ...
});
Node.js
const scoresRef = db.ref('scores');
scoresRef.orderByValue().on('value', (snapshot) => {
  snapshot.forEach((data) => {
    console.log('The ' + data.key + ' dinosaur\'s score is ' + data.val());
  });
});
פִּיתוֹן
ref = db.reference('scores')
snapshot = ref.order_by_value().get()
for key, val in snapshot.items():
    print('The {0} dinosaur\'s score is {1}'.format(key, val))
ללכת
ref := client.NewRef("scores")

results, err := ref.OrderByValue().GetOrdered(ctx)
if err != nil {
	log.Fatalln("Error querying database:", err)
}
for _, r := range results {
	var score int
	if err := r.Unmarshal(&score); err != nil {
		log.Fatalln("Error unmarshaling result:", err)
	}
	fmt.Printf("The %s dinosaur's score is %d\n", r.Key(), score)
}

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

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

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

הגבלת שאילתות

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

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

Java
dinosaursRef.orderByChild("weight").limitToLast(2).addChildEventListener(new ChildEventListener() {
  @Override
  public void onChildAdded(DataSnapshot dataSnapshot, String prevChildKey) {
    System.out.println(dataSnapshot.getKey());
  }

  // ...
});
Node.js
const ref = db.ref('dinosaurs');
ref.orderByChild('weight').limitToLast(2).on('child_added', (snapshot) => {
  console.log(snapshot.key);
});
פִּיתוֹן
ref = db.reference('dinosaurs')
snapshot = ref.order_by_child('weight').limit_to_last(2).get()
for key in snapshot:
    print(key)
ללכת
ref := client.NewRef("dinosaurs")

results, err := ref.OrderByChild("weight").LimitToLast(2).GetOrdered(ctx)
if err != nil {
	log.Fatalln("Error querying database:", err)
}
for _, r := range results {
	fmt.Println(r.Key())
}

ה-callback child_added מופעל בדיוק פעמיים, אלא אם יש פחות משני דינוזאורים המאוחסנים במסד הנתונים. הוא גם יפוטר עבור כל דינוזאור חדש וכבד יותר שיתווסף למסד הנתונים. ב-Python, השאילתה מחזירה ישירות OrderedDict המכיל את שני הדינוזאורים הכבדים ביותר.

באופן דומה, אתה יכול למצוא את שני הדינוזאורים הקצרים ביותר באמצעות limitToFirst() :

Java
dinosaursRef.orderByChild("weight").limitToFirst(2).addChildEventListener(new ChildEventListener() {
  @Override
  public void onChildAdded(DataSnapshot dataSnapshot, String prevChildKey) {
    System.out.println(dataSnapshot.getKey());
  }

  // ...
});
Node.js
const ref = db.ref('dinosaurs');
ref.orderByChild('height').limitToFirst(2).on('child_added', (snapshot) => {
  console.log(snapshot.key);
});
פִּיתוֹן
ref = db.reference('dinosaurs')
snapshot = ref.order_by_child('height').limit_to_first(2).get()
for key in snapshot:
    print(key)
ללכת
ref := client.NewRef("dinosaurs")

results, err := ref.OrderByChild("height").LimitToFirst(2).GetOrdered(ctx)
if err != nil {
	log.Fatalln("Error querying database:", err)
}
for _, r := range results {
	fmt.Println(r.Key())
}

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

אתה יכול גם לבצע שאילתות הגבלה עם orderByValue() . אם אתה רוצה ליצור טבלת הישגים עם 3 דינוזאורי ספורט הדינו בעלי הניקוד הגבוה ביותר, תוכל לעשות את הפעולות הבאות:

Java
scoresRef.orderByValue().limitToFirst(3).addChildEventListener(new ChildEventListener() {
  @Override
  public void onChildAdded(DataSnapshot dataSnapshot, String prevChildKey) {
    System.out.println("The " + dataSnapshot.getKey() + " score is " + dataSnapshot.getValue());
  }

  // ...
});
Node.js
const scoresRef = db.ref('scores');
scoresRef.orderByValue().limitToLast(3).on('value', (snapshot)  =>{
  snapshot.forEach((data) => {
    console.log('The ' + data.key + ' dinosaur\'s score is ' + data.val());
  });
});
פִּיתוֹן
scores_ref = db.reference('scores')
snapshot = scores_ref.order_by_value().limit_to_last(3).get()
for key, val in snapshot.items():
    print('The {0} dinosaur\'s score is {1}'.format(key, val))
ללכת
ref := client.NewRef("scores")

results, err := ref.OrderByValue().LimitToLast(3).GetOrdered(ctx)
if err != nil {
	log.Fatalln("Error querying database:", err)
}
for _, r := range results {
	var score int
	if err := r.Unmarshal(&score); err != nil {
		log.Fatalln("Error unmarshaling result:", err)
	}
	fmt.Printf("The %s dinosaur's score is %d\n", r.Key(), score)
}

שאילתות טווח

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

Java
dinosaursRef.orderByChild("height").startAt(3).addChildEventListener(new ChildEventListener() {
  @Override
  public void onChildAdded(DataSnapshot dataSnapshot, String prevChildKey) {
    System.out.println(dataSnapshot.getKey());
  }

  // ...
});
Node.js
const ref = db.ref('dinosaurs');
ref.orderByChild('height').startAt(3).on('child_added', (snapshot) => {
  console.log(snapshot.key);
});
פִּיתוֹן
ref = db.reference('dinosaurs')
snapshot = ref.order_by_child('height').start_at(3).get()
for key in snapshot:
    print(key)
ללכת
ref := client.NewRef("dinosaurs")

results, err := ref.OrderByChild("height").StartAt(3).GetOrdered(ctx)
if err != nil {
	log.Fatalln("Error querying database:", err)
}
for _, r := range results {
	fmt.Println(r.Key())
}

אתה יכול להשתמש ב- endAt() כדי למצוא את כל הדינוזאורים ששמם בא לפני Pterodactyl מבחינה לקסיקוגרפית:

Java
dinosaursRef.orderByKey().endAt("pterodactyl").addChildEventListener(new ChildEventListener() {
  @Override
  public void onChildAdded(DataSnapshot dataSnapshot, String prevChildKey) {
    System.out.println(dataSnapshot.getKey());
  }

  // ...
});
Node.js
const ref = db.ref('dinosaurs');
ref.orderByKey().endAt('pterodactyl').on('child_added', (snapshot) => {
  console.log(snapshot.key);
});
פִּיתוֹן
ref = db.reference('dinosaurs')
snapshot = ref.order_by_key().end_at('pterodactyl').get()
for key in snapshot:
    print(key)
ללכת
ref := client.NewRef("dinosaurs")

results, err := ref.OrderByKey().EndAt("pterodactyl").GetOrdered(ctx)
if err != nil {
	log.Fatalln("Error querying database:", err)
}
for _, r := range results {
	fmt.Println(r.Key())
}

אתה יכול לשלב startAt() ו- endAt() כדי להגביל את שני הקצוות של השאילתה שלך. הדוגמה הבאה מוצאת את כל הדינוזאורים ששמם מתחיל באות "ב":

Java
dinosaursRef.orderByKey().startAt("b").endAt("b\uf8ff").addChildEventListener(new ChildEventListener() {
  @Override
  public void onChildAdded(DataSnapshot dataSnapshot, String prevChildKey) {
    System.out.println(dataSnapshot.getKey());
  }

  // ...
});
Node.js
var ref = db.ref('dinosaurs');
ref.orderByKey().startAt('b').endAt('b\uf8ff').on('child_added', (snapshot) => {
  console.log(snapshot.key);
});
פִּיתוֹן
ref = db.reference('dinosaurs')
snapshot = ref.order_by_key().start_at('b').end_at(u'b\uf8ff').get()
for key in snapshot:
    print(key)
ללכת
ref := client.NewRef("dinosaurs")

results, err := ref.OrderByKey().StartAt("b").EndAt("b\uf8ff").GetOrdered(ctx)
if err != nil {
	log.Fatalln("Error querying database:", err)
}
for _, r := range results {
	fmt.Println(r.Key())
}

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

Java
dinosaursRef.orderByChild("height").equalTo(25).addChildEventListener(new ChildEventListener() {
  @Override
  public void onChildAdded(DataSnapshot dataSnapshot, String prevChildKey) {
    System.out.println(dataSnapshot.getKey());
  }

  // ...
});
Node.js
const ref = db.ref('dinosaurs');
ref.orderByChild('height').equalTo(25).on('child_added', (snapshot) => {
  console.log(snapshot.key);
});
פִּיתוֹן
ref = db.reference('dinosaurs')
snapshot = ref.order_by_child('height').equal_to(25).get()
for key in snapshot:
    print(key)
ללכת
ref := client.NewRef("dinosaurs")

results, err := ref.OrderByChild("height").EqualTo(25).GetOrdered(ctx)
if err != nil {
	log.Fatalln("Error querying database:", err)
}
for _, r := range results {
	fmt.Println(r.Key())
}

שאילתות טווח שימושיות גם כאשר אתה צריך לדמיין את הנתונים שלך.

מחברים את הכל ביחד

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

Java
dinosaursRef.child("stegosaurus").child("height").addValueEventListener(new ValueEventListener() {
  @Override
  public void onDataChange(DataSnapshot stegoHeightSnapshot) {
    Integer favoriteDinoHeight = stegoHeightSnapshot.getValue(Integer.class);
    Query query = dinosaursRef.orderByChild("height").endAt(favoriteDinoHeight).limitToLast(2);
    query.addValueEventListener(new ValueEventListener() {
      @Override
      public void onDataChange(DataSnapshot dataSnapshot) {
        // Data is ordered by increasing height, so we want the first entry
        DataSnapshot firstChild = dataSnapshot.getChildren().iterator().next();
        System.out.println("The dinosaur just shorter than the stegosaurus is: " + firstChild.getKey());
      }

      @Override
      public void onCancelled(DatabaseError databaseError) {
        // ...
      }
    });
  }

  @Override
  public void onCancelled(DatabaseError databaseError) {
    // ...
  }
});
Node.js
  const ref = db.ref('dinosaurs');
  ref.child('stegosaurus').child('height').on('value', (stegosaurusHeightSnapshot) => {
    const favoriteDinoHeight = stegosaurusHeightSnapshot.val();

    const queryRef = ref.orderByChild('height').endAt(favoriteDinoHeight).limitToLast(2);
    queryRef.on('value', (querySnapshot) => {
      if (querySnapshot.numChildren() === 2) {
        // Data is ordered by increasing height, so we want the first entry
        querySnapshot.forEach((dinoSnapshot) => {
          console.log('The dinosaur just shorter than the stegasaurus is ' + dinoSnapshot.key);

          // Returning true means that we will only loop through the forEach() one time
          return true;
        });
      } else {
        console.log('The stegosaurus is the shortest dino');
      }
    });
});
פִּיתוֹן
ref = db.reference('dinosaurs')
favotire_dino_height = ref.child('stegosaurus').child('height').get()
query = ref.order_by_child('height').end_at(favotire_dino_height).limit_to_last(2)
snapshot = query.get()
if len(snapshot) == 2:
    # Data is ordered by increasing height, so we want the first entry.
    # Second entry is stegosarus.
    for key in snapshot:
        print('The dinosaur just shorter than the stegosaurus is {0}'.format(key))
        return
else:
    print('The stegosaurus is the shortest dino')
ללכת
ref := client.NewRef("dinosaurs")

var favDinoHeight int
if err := ref.Child("stegosaurus").Child("height").Get(ctx, &favDinoHeight); err != nil {
	log.Fatalln("Error querying database:", err)
}

query := ref.OrderByChild("height").EndAt(favDinoHeight).LimitToLast(2)
results, err := query.GetOrdered(ctx)
if err != nil {
	log.Fatalln("Error querying database:", err)
}
if len(results) == 2 {
	// Data is ordered by increasing height, so we want the first entry.
	// Second entry is stegosarus.
	fmt.Printf("The dinosaur just shorter than the stegosaurus is %s\n", results[0].Key())
} else {
	fmt.Println("The stegosaurus is the shortest dino")
}

כיצד מסודרים נתונים

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

orderByChild

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

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

orderByKey

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

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

orderByValue

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