Daten werden abgerufen

In diesem Dokument werden die Grundlagen des Abrufens von Datenbankdaten, die Reihenfolge der Daten und die Durchführung einfache Datenabfragen an. Die Datenabfrage im Admin SDK wird in verschiedenen Programmiersprachen etwas unterschiedlich implementiert.

  1. Asynchrone Listener: In Firebase Realtime Database gespeicherte Daten werden durch Anhängen eines asynchronen Listeners an einen Datenbankreferenz. Der Listener wird einmal für den anfänglichen Status der Daten ausgelöst und wenn sich die Daten ändern. Ein Ereignis-Listener kann mehrere Ereignistypen empfangen. Dieser Modus des Datenabrufs wird unterstützt in Java, Node.js und Python Admin SDKs.
  2. Lesezugriff blockieren: In einer Firebase Realtime Database gespeicherte Daten werden durch Aufrufen einer Blockierungsmethode für eine Datenbank abgerufen reference, das die bei der Referenz gespeicherten Daten zurückgibt. Jeder Methodenaufruf ist ein einmaliger Vorgang. Das SDK registriert also keine Callbacks, die auf nachfolgende Datenaktualisierungen warten. Dieses Modell des Datenabrufs wird in Python und Go Admin SDKs unterstützt.

Einstieg

Sehen wir uns noch einmal das Blogging-Beispiel aus dem vorherigen Artikel an, um zu verstehen, wie Daten aus einer Firebase-Datenbank gelesen werden. Die Blogbeiträge 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 ab:

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 Code oben ausführen, sehen Sie ein Objekt mit allen Ihren Beiträgen, die in der Konsole protokolliert wurden. Bei Node.js und Java wird die Listenerfunktion immer dann aufgerufen, wenn Ihrer Datenbankreferenz neue Daten hinzugefügt werden. Dazu müssen Sie keinen zusätzlichen Code schreiben.

In Java und Node.js empfängt die Callback-Funktion ein DataSnapshot, bei dem es sich um einen Snapshot der Daten handelt. Ein Snapshot ist ein Bild der Daten in einer bestimmten Datenbankreferenz zu einem bestimmten Zeitpunkt. Durch den Aufruf von val() / getValue() für einen Snapshot wird eine sprachspezifische Objektdarstellung der Daten zurückgegeben. Wenn an der Referenzposition keine Daten vorhanden sind, hat der Snapshot den Wert null. Die Methode get() in Python gibt direkt eine Python-Darstellung der Daten zurück. Die Funktion Get() in Go löscht die Daten in einer bestimmten Datenstruktur.

Beachten Sie, dass wir im obigen Beispiel den Ereignistyp value verwendet haben, mit dem der gesamte Inhalt einer Firebase-Datenbankreferenz gelesen wird, auch wenn nur ein Datenelement geändert wurde. value ist einer der fünf unten aufgeführten Ereignistypen, mit denen Sie Daten aus der Datenbank lesen können.

Ereignistypen in Java und Node.js lesen

Wert

Mit dem Ereignis value wird ein statischer Snapshot des Inhalts unter einem bestimmten Datenbankpfad gelesen, der zum Zeitpunkt des Leseereignisses vorhanden war. Sie wird einmal mit den ursprünglichen Daten und noch einmal jedes Mal ausgelöst, wenn sich die Daten ändern. Dem Ereignis-Callback wird ein Snapshot übergeben, der alle Daten an diesem Ort, einschließlich untergeordneter Daten, enthält. Im Codebeispiel oben hat value alle Blogposts in Ihrer App zurückgegeben. Jedes Mal, wenn ein neuer Blogpost hinzugefügt wird, gibt die Callback-Funktion alle Posts zurück.

Untergeordneter hinzugefügt

Das Ereignis child_added wird in der Regel beim Abrufen einer Liste von Elementen aus der Datenbank verwendet. Im Gegensatz zu value, das den gesamten Inhalt des Standorts zurückgibt, wird child_added einmal für jedes vorhandene untergeordnete Element und dann jedes Mal ausgelöst, wenn dem angegebenen Pfad ein neues untergeordnetes Element hinzugefügt wird. Dem Ereignis-Callback wird ein Snapshot übergeben, der die Daten des neuen untergeordneten Elements enthält. Zu Sortierzwecken wird ihm 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 Blog-App hinzugefügt wird, 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 Blogpost. Da das SDK Posts durch Abrufen des Werts in Objekte konvertiert, haben Sie Zugriff auf die Eigenschaften „author“ und „title“ des Posts, indem Sie author bzw. title aufrufen. Außerdem haben Sie über das zweite prevChildKey-Argument Zugriff auf die vorherige Beitrags-ID.

Kind geändert

Das Ereignis child_changed wird jedes Mal ausgelöst, wenn ein untergeordneter Knoten geändert wird. Dazu gehören auch alle Änderungen an Nachkommen des untergeordneten Knotens. Wird normalerweise in Verbindung mit child_added verwendet und child_removed, um auf Änderungen an einer Liste von Elementen zu reagieren. Der an den Ereignis-Callback übergebene Snapshot enthält die aktualisierten Daten für das untergeordnete Element.

Mit child_changed können Sie aktualisierte Daten zu Blogposts lesen, wenn diese 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 child_removed-Ereignis wird ausgelöst, wenn ein sofortiges untergeordnetes Element entfernt wird. Sie wird in der Regel in Kombination mit child_added und child_changed verwendet. Der an den Ereignis-Callback übergebene Snapshot enthält die Daten für das entfernte untergeordnete Element.

Im Blogbeispiel können Sie mit child_removed eine Benachrichtigung über den gelöschten Post 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 verschoben

Das Ereignis child_moved wird bei der Arbeit mit sortierten Daten verwendet, die im nächsten Abschnitt behandelt werden.

Veranstaltungsgarantie

Die Firebase-Datenbank bietet mehrere wichtige Garantien für 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 Abläufe oder führen zu vorübergehenden Abweichungen, z. B. durch vorübergehende Unterbrechung der Netzwerkverbindung.
Schreibvorgänge von einem einzelnen Client werden immer auf den Server geschrieben und der Reihe nach an andere Nutzer gesendet.
Wertereignisse werden immer zuletzt ausgelöst und enthalten garantiert Aktualisierungen von anderen Ereignissen, die aufgetreten sind bevor dieser Snapshot erstellt wurde.

Da Wert-Ereignisse immer als letztes 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

Zum Entfernen von Callbacks werden der Ereignistyp und die zu entfernende Callback-Funktion wie folgt angegeben:

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 Callbacks übergeben werden:

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

Wenn Sie alle Callbacks von einem Standort entfernen möchten, können Sie Folgendes tun:

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, die Ihnen dabei hilft:

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. Zum Erstellen einer Abfrage in Ihrer Datenbank geben Sie zuerst an, wie die Daten sortiert werden sollen. Verwenden Sie dazu eine der Sortierfunktionen: orderByChild(), orderByKey() oder orderByValue(). Sie können diese Methoden dann mit fünf anderen Methoden kombinieren, um komplexe Abfragen durchzuführen: limitToFirst(), limitToLast(), startAt(), endAt() und equalTo().

Da wir alle bei Firebase denken, dass Dinosaurier sehr cool sind, verwenden wir ein Snippet aus einer Beispieldatenbank mit Fakten über Dinosaurier, 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, Schlüssel oder Wert. Eine einfache Datenbankabfrage beginnt mit einer dieser Sortierfunktionen, die unten erläutert werden.

Nach einem angegebenen untergeordneten Schlüssel sortieren

Sie können Knoten nach einem gemeinsamen untergeordneten Schlüssel ordnen, indem Sie diesen Schlüssel an orderByChild() übergeben. Wenn Sie beispielsweise alle Dinosaurier nach Größe sortiert abrufen möchten, gehen Sie so vor:

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('{0} was {1} meters tall'.format(key, val))
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)
}

Jeder Knoten, der nicht den untergeordneten Schlüssel für die Abfrage hat, wird mit dem Wert null sortiert, d. h., er steht in der Reihenfolge an erster Stelle. Weitere Informationen zur Sortierung von Daten finden Sie im Abschnitt Wie Daten sortiert werden.

Abfragen können auch nach tief verschachtelten untergeordneten Elementen geordnet werden, anstatt nur nach untergeordneten Elementen. Dies ist nützlich, wenn Sie tief verschachtelte Daten wie die folgende haben:

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

Um die Höhe jetzt abzufragen, 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('{0} was {1} meters tall'.format(key, val))
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. orderByChild() wird angerufen mehrfach in derselben Abfrage löst einen Fehler aus.

Nach Schlüssel sortieren

Mit der Methode orderByKey() können Sie Knoten auch nach ihren Schlüsseln sortieren. Die 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. Nehmen wir an, die Dinosaurier haben einen Dino-Sportwettkampf und Sie verfolgen ihre Ergebnisse im folgenden Format:

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

Um die Dinosaurier nach ihrer Punktzahl zu sortieren, könnten 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('The {0} dinosaur\'s score is {1}'.format(key, 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 Wie Daten geordnet werden finden Sie eine Erklärung, wie null, boolesche Werte, Strings und Objektwerte bei der Verwendung von orderByValue() sortiert werden.

Komplexe Abfragen

Da jetzt klar ist, wie die Daten angeordnet sind, können Sie mit den unten beschriebenen Methoden limit und range komplexere Abfragen erstellen.

Abfragen begrenzen

Mit den Abfragen limitToFirst() und limitToLast() wird ein Maximale Anzahl von untergeordneten Elementen, 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, jeweils ein child_added-Ereignis . Wenn Sie jedoch mehr als 100 Nachrichten haben, erhalten Sie nur eine child_added 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, erhältst du child_added Ereignisse für Elemente, die die Abfrage eingeben, und child_removed-Ereignisse für Elemente, die sie verlassen, sodass die Gesamtzahl bei 100 bleibt.

Mithilfe der Faktendatenbank der Dinosaurier und orderByChild() können Sie die beiden schwersten Dinosaurier:

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, es sei denn, in der Datenbank sind weniger als zwei Dinosaurier gespeichert. Sie wird außerdem für jeden neuen, schwereren Dinosaurier abgefeuert, der der Datenbank hinzugefügt wird. In Python gibt die Abfrage direkt eine OrderedDict mit den beiden schwersten Dinosauriern zurück.

Ebenso können Sie limitToFirst() verwenden, um die beiden kürzesten Dinosaurier zu 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, es sei denn, in der Datenbank sind weniger als zwei Dinosaurier gespeichert. Außerdem wird er noch einmal ausgelöst, wenn einer der ersten beiden Dinosaurier aus der Datenbank entfernt wird, da ein neuer Dinosaurier jetzt der zweitkürzeste ist. In Python gibt die Abfrage direkt ein OrderedDict zurück, das die kürzesten Dinosaurier enthält.

Sie können Limit-Abfragen auch mit orderByValue() durchführen. Wenn Sie eine Bestenliste mit den drei Dino-Sport-Dinosauriern mit der höchsten Punktzahl erstellen möchten, können Sie so vorgehen:

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('The {0} dinosaur\'s score is {1}'.format(key, 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, die mindestens drei Meter groß sind, kannst du orderByChild() kombinieren und 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);
});
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() kannst du nach allen Dinosauriern suchen, deren Namen vor Pterodaktylus stehen lexikografisch:

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 Abfrage. 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(u'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 es mit den anderen Bereichsabfragen wird er für jeden übereinstimmenden untergeordneten Knoten ausgelöst. So können Sie zum Beispiel verwenden Sie die folgende Abfrage, um alle 25 Meter hohen Dinosaurier zu ermitteln:

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. So finden Sie beispielsweise der Name des Dinosauriers, der einfach kürzer ist als der Stegosaurus:

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('The dinosaur just shorter than the stegosaurus is {0}'.format(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")
}

Wie Daten geordnet werden

In diesem Abschnitt wird erläutert, wie Ihre Daten bei Verwendung jeder der vier Sortierfunktionen geordnet werden.

orderByChild

Bei Verwendung von orderByChild() werden Daten, die den angegebenen untergeordneten Schlüssel enthalten, so sortiert:

  1. Untergeordnete Elemente mit einem null-Wert für den angegebenen untergeordneten Schlüssel werden zuerst angezeigt.
  2. Als Nächstes kommen untergeordnete Elemente mit dem Wert false für den angegebenen untergeordneten Schlüssel. Wenn mehrere untergeordnete Elemente den Wert false haben, werden sie lexikografisch nach Schlüssel sortiert.
  3. Als Nächstes folgen untergeordnete Elemente mit dem Wert true für den angegebenen untergeordneten Schlüssel. Wenn mehrere untergeordnete Elemente den Wert true haben, werden sie nach Schlüssel sortiert.
  4. Untergeordnete Elemente mit einem numerischen Wert kommen als Nächstes, in aufsteigender Reihenfolge sortiert. Wenn mehrere untergeordnete Elemente denselben numerischen Wert für den angegebenen untergeordneten Knoten haben, werden sie nach Schlüssel sortiert.
  5. Zeichenfolgen stehen nach 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.
  6. Objekte kommen an letzter Stelle und werden lexikografisch nach Schlüssel in aufsteigender Reihenfolge sortiert.

orderByKey

Wenn Sie orderByKey() zum Sortieren Ihrer Daten verwenden, werden die Daten so in aufsteigender Reihenfolge nach Schlüssel zurückgegeben. Denken Sie daran, dass Schlüssel nur Strings sein können.

  1. Untergeordnete Elemente mit einem Schlüssel, der als 32-Bit-Ganzzahl geparst werden kann, stehen an erster Stelle, und zwar in aufsteigender Reihenfolge.
  2. Als Nächstes kommen untergeordnete Elemente, deren Schlüssel einen Stringwert enthält. Sie werden lexikografisch in aufsteigender Reihenfolge sortiert.

orderByValue

Wenn Sie orderByValue() verwenden, werden die untergeordneten Elemente nach ihrem Wert sortiert. Die Sortierungskriterien sind dieselben wie in orderByChild(), mit der Ausnahme, dass der Wert des Knotens anstelle des Werts eines angegebenen untergeordneten Schlüssels verwendet wird.