In diesem Dokument werden die Grundlagen des Abrufens von Datenbankdaten, der Reihenfolge von Daten und der Durchführung einfacher Abfragen für Daten behandelt. Das Abrufen von Daten im Admin SDK wird in den verschiedenen Programmiersprachen etwas anders implementiert.
- Asynchrone Listener:Daten, die in einem Firebase Realtime Database gespeichert sind, werden abgerufen, indem ein asynchroner Listener an eine Datenbankreferenz angehängt wird. Der Listener wird einmal für den anfänglichen Status der Daten und dann bei jeder Änderung der Daten ausgelöst. Ein Ereignis-Listener kann mehrere verschiedene Ereignistypen empfangen. Diese Art des Datenabrufs wird in den Admin SDKs für Java, Node.js und Python unterstützt.
- Blockierende Lesevorgänge:Daten, die in einem Firebase Realtime Database gespeichert sind, werden abgerufen, indem eine blockierende Methode für eine Datenbankreferenz aufgerufen wird, die die an der Referenz gespeicherten Daten zurückgibt. Jeder Methodenaufruf ist ein einmaliger Vorgang. Das bedeutet, dass das SDK keine Callbacks registriert, die auf nachfolgende Datenaktualisierungen warten. Dieses Modell des Datenabrufs wird in den Python- und Go-Admin-SDKs unterstützt.
Einstieg
Sehen wir uns noch einmal das Beispiel mit dem Bloggen aus dem vorherigen Artikel an, um zu verstehen, wie Daten aus einer Firebase-Datenbank gelesen werden. Die Blogposts in der Beispiel-App werden unter der Datenbank-URL https://docs-examples.firebaseio.com/server/saving-data/fireblog/posts.json gespeichert. So rufen Sie Ihre Beitragsdaten auf:
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); });
Python
# 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())
Go
// 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) }
Wenn Sie den oben genannten Code ausführen, wird ein Objekt mit allen Ihren Beiträgen in der Konsole protokolliert. Bei Node.js und Java wird die Listener-Funktion immer dann aufgerufen, wenn Ihrer Datenbankreferenz neue Daten hinzugefügt werden. Sie müssen keinen zusätzlichen Code schreiben, damit dies geschieht.
In Java und Node.js empfängt die Callback-Funktion ein DataSnapshot
, das ein Snapshot der Daten ist. Ein Snapshot ist ein Bild der Daten zu einem bestimmten Datenbankverweis zu einem bestimmten Zeitpunkt. Wenn Sie val()
/ getValue()
für einen Snapshot aufrufen, wird eine sprachspezifische Objektdarstellung der Daten zurückgegeben. Wenn am Speicherort der Referenz keine Daten vorhanden sind, ist der Wert des Snapshots null
. Die Methode get()
in Python gibt direkt eine Python-Darstellung der Daten zurück. Die Funktion Get()
in Go entpackt die Daten in eine bestimmte Datenstruktur.
Im obigen Beispiel haben wir den Ereignistyp value
verwendet. Damit wird der gesamte Inhalt einer Firebase-Datenbankreferenz gelesen, auch wenn sich nur ein Datenelement geändert hat. value
ist einer der fünf verschiedenen Ereignistypen, die unten aufgeführt sind und mit denen Sie Daten aus der Datenbank lesen können.
Ereignistypen in Java und Node.js lesen
Wert
Mit dem value
-Ereignis wird ein statischer Snapshot des Inhalts an einem bestimmten Datenbankpfad gelesen, wie er zum Zeitpunkt des Leseereignisses vorhanden war. Er wird einmal mit den ursprünglichen Daten und dann bei jeder Änderung der Daten ausgelöst. An den Ereignis-Callback wird ein Snapshot mit allen Daten an diesem Speicherort, einschließlich untergeordneter Daten, übergeben. Im obigen Codebeispiel hat value
alle Blogbeiträge in Ihrer App zurückgegeben. Jedes Mal, wenn ein neuer Blogbeitrag hinzugefügt wird, gibt die Callback-Funktion alle Beiträge zurück.
Kind hinzugefügt
Das Ereignis child_added
wird in der Regel verwendet, wenn eine Liste von Elementen aus der Datenbank abgerufen wird. Im Gegensatz zu value
, das den gesamten Inhalt des Speicherorts zurückgibt, wird child_added
einmal für jedes vorhandene untergeordnete Element und dann noch einmal jedes Mal ausgelöst, wenn dem angegebenen Pfad ein neues untergeordnetes Element hinzugefügt wird. An den Ereignis-Callback wird ein Snapshot mit den Daten des neuen untergeordneten Elements übergeben. Für die Sortierung wird auch ein zweites Argument übergeben, das den Schlüssel des vorherigen untergeordneten Elements enthält.
Wenn Sie nur die Daten zu jedem neuen Beitrag abrufen möchten, der Ihrer Blogging-App hinzugefügt wurde, können Sie child_added
verwenden:
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); });
In diesem Beispiel enthält der Snapshot ein Objekt mit einem einzelnen Blogbeitrag. Da das SDK Beiträge in Objekte umwandelt, indem es den Wert abruft, können Sie auf die Eigenschaften „Autor“ und „Titel“ des Beitrags zugreifen, indem Sie author
bzw. title
aufrufen. Über das zweite prevChildKey
-Argument haben Sie auch Zugriff auf die vorherige Post-ID.
Kind geändert
Das child_changed
-Ereignis wird immer dann ausgelöst, wenn ein untergeordneter Knoten geändert wird. Das gilt auch für alle Änderungen an untergeordneten Knoten des untergeordneten Knotens. Sie wird in der Regel in Verbindung mit child_added
und child_removed
verwendet, um auf Änderungen an einer Liste von Elementen zu reagieren. Der Snapshot, der an den Ereignis-Callback übergeben wird, enthält die aktualisierten Daten für das untergeordnete Element.
Mit child_changed
können Sie aktualisierte Daten zu Blogposts lesen, wenn sie bearbeitet werden:
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); });
Kind entfernt
Das Ereignis child_removed
wird ausgelöst, wenn ein direktes untergeordnetes Element entfernt wird. Sie wird in der Regel in Verbindung mit child_added
und child_changed
verwendet. Der Snapshot, der an den Ereignis-Callback übergeben wird, enthält die Daten für das entfernte untergeordnete Element.
Im Blogbeispiel können Sie mit child_removed
eine Benachrichtigung über den gelöschten Beitrag in der Konsole protokollieren:
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'); });
Kind ist umgezogen
Das child_moved
-Ereignis wird verwendet, wenn Sie mit sortierten Daten arbeiten. Das wird im nächsten Abschnitt behandelt.
Event-Garantien
Die Firebase-Datenbank bietet mehrere wichtige Garantien in Bezug auf Ereignisse:
Garantien für Datenbankereignisse |
---|
Ereignisse werden immer ausgelöst, wenn sich der lokale Status ändert. |
Ereignisse spiegeln immer den korrekten Status der Daten wider, auch wenn lokale Vorgänge oder das Timing vorübergehende Unterschiede verursachen, z. B. bei einem vorübergehenden Verlust der Netzwerkverbindung. |
Schreibvorgänge von einem einzelnen Client werden immer auf den Server geschrieben und in der richtigen Reihenfolge an andere Nutzer übertragen. |
Wert-Ereignisse werden immer zuletzt ausgelöst und enthalten garantiert Aktualisierungen aus allen anderen Ereignissen, die vor der Erstellung des Snapshots aufgetreten sind. |
Da Wert-Ereignisse immer zuletzt ausgelöst werden, funktioniert das folgende Beispiel immer:
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); });
Callbacks trennen
Callbacks werden entfernt, indem der Ereignistyp und die zu entfernende Callback-Funktion angegeben werden, wie im Folgenden:
Java
// Create and attach listener ValueEventListener listener = new ValueEventListener() { // ... }; ref.addValueEventListener(listener); // Remove listener ref.removeEventListener(listener);
Node.js
ref.off('value', originalCallback);
Wenn Sie einen Bereichskontext an on()
übergeben haben, muss er beim Trennen des Rückrufs übergeben werden:
Java
// Not applicable for Java
Node.js
ref.off('value', originalCallback, ctx);
Wenn Sie alle Rückrufe an einem Standort entfernen möchten, können Sie so vorgehen:
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();
Daten einmal lesen
In einigen Fällen kann es nützlich sein, einen Rückruf einmal aufzurufen und dann sofort zu entfernen. Wir haben eine Hilfsfunktion erstellt, um dies zu vereinfachen:
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 });
Python
# 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())
Go
// 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) }
Daten abfragen
Mit Firebase-Datenbankabfragen können Sie Daten basierend auf verschiedenen Faktoren selektiv abrufen. Um eine Abfrage in Ihrer Datenbank zu erstellen, geben Sie zuerst an, wie Ihre Daten sortiert werden sollen. Verwenden Sie dazu eine der Sortierfunktionen: orderByChild()
, orderByKey()
oder orderByValue()
. Sie können diese dann mit fünf anderen Methoden kombinieren, um komplexe Abfragen durchzuführen: limitToFirst()
, limitToLast()
, startAt()
, endAt()
und equalTo()
.
Da wir alle bei Firebase Dinosaurier ziemlich cool finden, verwenden wir einen Ausschnitt aus einer Beispieldatenbank mit Dinosaurierfakten, um zu zeigen, wie Sie Daten in Ihrer Firebase-Datenbank abfragen können:
{ "lambeosaurus": { "height" : 2.1, "length" : 12.5, "weight": 5000 }, "stegosaurus": { "height" : 4, "length" : 9, "weight" : 2500 } }
Sie können Daten auf drei Arten sortieren: nach untergeordnetem Schlüssel, nach Schlüssel oder nach Wert. Eine einfache Datenbankabfrage beginnt mit einer dieser Sortierfunktionen, die jeweils unten erläutert werden.
Nach einem bestimmten untergeordneten Schlüssel sortieren
Sie können Knoten nach einem gemeinsamen untergeordneten Schlüssel sortieren, indem Sie diesen Schlüssel an orderByChild()
übergeben. Wenn Sie beispielsweise alle Dinosaurier nach Größe sortiert lesen möchten, können Sie Folgendes tun:
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'); });
Python
ref = db.reference('dinosaurs') snapshot = ref.order_by_child('height').get() for key, val in snapshot.items(): print(f'{key} was {val} meters tall')
Go
// 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) }
Alle Knoten, die nicht den untergeordneten Schlüssel haben, nach dem wir suchen, werden mit dem Wert null
sortiert. Das bedeutet, dass sie in der Reihenfolge an erster Stelle stehen. Weitere Informationen dazu, wie Daten sortiert werden, finden Sie im Abschnitt So werden Daten sortiert.
Abfragen können auch nach untergeordneten Elementen mit mehreren Ebenen sortiert werden, nicht nur nach untergeordneten Elementen einer Ebene. Das ist nützlich, wenn Sie tief verschachtelte Daten wie die folgenden haben:
{ "lambeosaurus": { "dimensions": { "height" : 2.1, "length" : 12.5, "weight": 5000 } }, "stegosaurus": { "dimensions": { "height" : 4, "length" : 9, "weight" : 2500 } } }
Wenn Sie jetzt die Höhe abfragen möchten, können Sie den vollständigen Pfad zum Objekt anstelle eines einzelnen Schlüssels verwenden:
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'); });
Python
ref = db.reference('dinosaurs') snapshot = ref.order_by_child('dimensions/height').get() for key, val in snapshot.items(): print(f'{key} was {val} meters tall')
Go
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) }
Abfragen können jeweils nur nach einem Schlüssel sortiert werden. Wenn orderByChild()
für dieselbe Abfrage mehrmals aufgerufen wird, wird ein Fehler ausgegeben.
Nach Schlüssel sortieren
Sie können Knoten auch mit der orderByKey()
-Methode nach ihren Schlüsseln sortieren. Im folgenden Beispiel werden alle Dinosaurier in alphabetischer Reihenfolge gelesen:
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); });
Python
ref = db.reference('dinosaurs') snapshot = ref.order_by_key().get() print(snapshot)
Go
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)
Nach Wert sortieren
Mit der Methode orderByValue()
können Sie Knoten nach dem Wert ihrer untergeordneten Schlüssel sortieren. Angenommen, die Dinosaurier veranstalten einen Dino-Sportwettbewerb und du hältst ihre Ergebnisse im folgenden Format fest:
{ "scores": { "bruhathkayosaurus" : 55, "lambeosaurus" : 21, "linhenykus" : 80, "pterodactyl" : 93, "stegosaurus" : 5, "triceratops" : 22 } }
Wenn Sie die Dinosaurier nach ihrem Score sortieren möchten, können Sie die folgende Abfrage erstellen:
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()); }); });
Python
ref = db.reference('scores') snapshot = ref.order_by_value().get() for key, val in snapshot.items(): print(f'The {key} dinosaur\'s score is {val}')
Go
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) }
Im Abschnitt So werden Daten sortiert wird erläutert, wie null
-, boolesche, String- und Objektwerte bei Verwendung von orderByValue()
sortiert werden.
Komplexe Anfragen
Nachdem Sie nun wissen, wie Ihre Daten sortiert sind, können Sie die unten beschriebenen Methoden limit oder range verwenden, um komplexere Abfragen zu erstellen.
Abfragen begrenzen
Mit den Abfragen limitToFirst()
und limitToLast()
wird eine maximale Anzahl von untergeordneten Elementen festgelegt, die für einen bestimmten Callback synchronisiert werden sollen. Wenn Sie ein Limit von 100 festlegen, erhalten Sie anfangs nur bis zu 100 child_added
-Ereignisse. Wenn Sie weniger als 100 Nachrichten in Ihrer Datenbank gespeichert haben, wird für jede Nachricht ein child_added
-Ereignis ausgelöst. Wenn Sie jedoch mehr als 100 Nachrichten haben, erhalten Sie nur ein child_added
-Ereignis für 100 dieser Nachrichten. Das sind die ersten 100 sortierten Nachrichten, wenn Sie limitToFirst()
verwenden, oder die letzten 100 sortierten Nachrichten, wenn Sie limitToLast()
verwenden. Wenn sich Artikel ändern, erhalten Sie child_added
-Ereignisse für Artikel, die in die Abfrage aufgenommen werden, und child_removed
-Ereignisse für Artikel, die aus der Abfrage entfernt werden. Die Gesamtzahl bleibt also bei 100.
Mithilfe der Dinosaurier-Faktenbank und orderByChild()
können Sie die beiden schwersten Dinosaurier finden:
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); });
Python
ref = db.reference('dinosaurs') snapshot = ref.order_by_child('weight').limit_to_last(2).get() for key in snapshot: print(key)
Go
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()) }
Der child_added
-Callback wird genau zweimal ausgelöst, sofern nicht weniger als zwei Dinosaurier in der Datenbank gespeichert sind. Sie wird auch für jeden neuen, schwereren Dinosaurier ausgelöst, der der Datenbank hinzugefügt wird.
In Python gibt die Abfrage direkt ein OrderedDict
mit den beiden schwersten Dinosauriern zurück.
Ebenso können Sie die beiden kürzesten Dinosaurier mit limitToFirst()
finden:
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); });
Python
ref = db.reference('dinosaurs') snapshot = ref.order_by_child('height').limit_to_first(2).get() for key in snapshot: print(key)
Go
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()) }
Der child_added
-Callback wird genau zweimal ausgelöst, sofern nicht weniger als zwei Dinosaurier in der Datenbank gespeichert sind. Das Ereignis wird auch noch einmal ausgelöst, wenn einer der ersten beiden Dinosaurier aus der Datenbank entfernt wird, da dann ein neuer Dinosaurier der zweitkürzeste ist. In Python gibt die Abfrage direkt ein OrderedDict
mit den kleinsten Dinosauriern zurück.
Sie können auch Limit-Abfragen mit orderByValue()
durchführen. Wenn Sie eine Bestenliste mit den drei Dinosauriern mit den höchsten Punktzahlen erstellen möchten, können Sie Folgendes tun:
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()); }); });
Python
scores_ref = db.reference('scores') snapshot = scores_ref.order_by_value().limit_to_last(3).get() for key, val in snapshot.items(): print(f'The {key} dinosaur\'s score is {val}')
Go
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) }
Bereichsabfragen
Mit startAt()
, endAt()
und equalTo()
können Sie beliebige Start- und Endpunkte für Ihre Abfragen auswählen. Wenn Sie beispielsweise alle Dinosaurier finden möchten, die mindestens drei Meter groß sind, können Sie orderByChild()
und startAt()
kombinieren:
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); });
Python
ref = db.reference('dinosaurs') snapshot = ref.order_by_child('height').start_at(3).get() for key in snapshot: print(key)
Go
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()) }
Mit endAt()
können Sie alle Dinosaurier finden, deren Namen lexikografisch vor „Pterodactyl“ stehen:
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); });
Python
ref = db.reference('dinosaurs') snapshot = ref.order_by_key().end_at('pterodactyl').get() for key in snapshot: print(key)
Go
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()) }
Sie können startAt()
und endAt()
kombinieren, um beide Enden Ihrer Abfrage einzuschränken. Im folgenden Beispiel werden alle Dinosaurier gefunden, deren Name mit dem Buchstaben „b“ beginnt:
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); });
Python
ref = db.reference('dinosaurs') snapshot = ref.order_by_key().start_at('b').end_at('b\uf8ff').get() for key in snapshot: print(key)
Go
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()) }
Mit der Methode equalTo()
können Sie nach genauen Übereinstimmungen filtern. Wie bei den anderen Bereichsabfragen wird sie für jeden übereinstimmenden untergeordneten Knoten ausgelöst. Mit der folgenden Abfrage können Sie beispielsweise alle 25 Meter hohen Dinosaurier finden:
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); });
Python
ref = db.reference('dinosaurs') snapshot = ref.order_by_child('height').equal_to(25).get() for key in snapshot: print(key)
Go
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()) }
Bereichsabfragen sind auch nützlich, wenn Sie Ihre Daten paginieren müssen.
Zusammenfassung
Sie können alle diese Techniken kombinieren, um komplexe Abfragen zu erstellen. Sie können beispielsweise den Namen des Dinosauriers finden, der nur kürzer als Stegosaurus ist:
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'); } }); });
Python
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(f'The dinosaur just shorter than the stegosaurus is {key}') return else: print('The stegosaurus is the shortest dino')
Go
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") }
So werden Daten sortiert
In diesem Abschnitt wird beschrieben, wie Ihre Daten bei Verwendung der einzelnen der vier Sortierfunktionen sortiert werden.
orderByChild
Wenn Sie orderByChild()
verwenden, werden Daten, die den angegebenen untergeordneten Schlüssel enthalten, so sortiert:
- Kinder mit einem
null
-Wert für den angegebenen untergeordneten Schlüssel werden zuerst angezeigt. - Als Nächstes folgen untergeordnete Elemente mit dem Wert
false
für den angegebenen untergeordneten Schlüssel. Wenn mehrere untergeordnete Elemente den Wertfalse
haben, werden sie lexikografisch nach Schlüssel sortiert. - Als Nächstes folgen untergeordnete Elemente mit dem Wert
true
für den angegebenen untergeordneten Schlüssel. Wenn mehrere untergeordnete Elemente den Werttrue
haben, werden sie lexikografisch nach Schlüssel sortiert. - Als Nächstes folgen untergeordnete Elemente mit einem numerischen Wert, sortiert in aufsteigender Reihenfolge. Wenn mehrere untergeordnete Elemente denselben numerischen Wert für den angegebenen untergeordneten Knoten haben, werden sie nach Schlüssel sortiert.
- Strings folgen auf Zahlen und werden lexikografisch in aufsteigender Reihenfolge sortiert. Wenn mehrere untergeordnete Elemente denselben Wert für den angegebenen untergeordneten Knoten haben, werden sie lexikografisch nach Schlüssel sortiert.
- Objekte werden zuletzt angezeigt und lexikografisch nach Schlüssel in aufsteigender Reihenfolge sortiert.
orderByKey
Wenn Sie orderByKey()
zum Sortieren Ihrer Daten verwenden, werden die Daten in aufsteigender Reihenfolge nach Schlüssel zurückgegeben: Schlüssel können nur Strings sein.
- Untergeordnete Elemente mit einem Schlüssel, der als 32-Bit-Ganzzahl geparst werden kann, werden zuerst in aufsteigender Reihenfolge sortiert.
- Als Nächstes folgen Kinder mit einem Stringwert als Schlüssel, die lexikografisch in aufsteigender Reihenfolge sortiert sind.
orderByValue
Bei Verwendung von orderByValue()
werden untergeordnete Elemente nach ihrem Wert sortiert. Die Sortierkriterien sind dieselben wie in orderByChild()
, mit der Ausnahme, dass der Wert des Knotens anstelle des Werts eines angegebenen untergeordneten Schlüssels verwendet wird.