Pobieram dane

Dokument ten zawiera podstawowe informacje o pobieraniu danych z bazy danych, ich sortowaniu oraz wykonywaniu prostych zapytań. Pobieranie danych w pakiecie Admin SDK działa nieco inaczej w zależności od języka programowania.

  1. Asynchroniczne odbiorniki: dane przechowywane w elementach Firebase Realtime Database są pobierane przez dołączenie asynchronicznego odbiornika do odwołania do bazy danych. Listener jest wywoływany raz w przypadku początkowego stanu danych i ponownie za każdym razem, gdy dane ulegną zmianie. Detektor zdarzeń może otrzymywać kilka różnych typów zdarzeń. Ten tryb pobierania danych jest obsługiwany w pakietach SDK dla administratorów w językach Java, Node.js i Python.
  2. Blokowanie odczytów: dane przechowywane w Firebase Realtime Database są pobierane przez wywołanie metody blokującej w odniesieniu do bazy danych, która zwraca dane przechowywane w tym odniesieniu. Każde wywołanie metody jest operacją jednorazową. Oznacza to, że SDK nie rejestruje żadnych funkcji wywołania zwrotnego, które nasłuchują kolejnych aktualizacji danych. Ten model pobierania danych jest obsługiwany w pakietach SDK Python i Go Admin.

Pierwsze kroki

Aby dowiedzieć się, jak odczytywać dane z bazy danych Firebase, zwróćmy uwagę na przykład z blogowania z poprzedniego artykułu. Przypominamy, że posty na blogu w przykładowej aplikacji są przechowywane pod adresem URL bazy danych https://docs-examples.firebaseio.com/server/saving-data/fireblog/posts.json. Aby odczytać dane posta, możesz wykonać te czynności:

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)
}

Po uruchomieniu tego kodu zobaczysz obiekt zawierający wszystkie posty zapisane w konsoli. W przypadku Node.js i Java funkcja listener jest wywoływana za każdym razem, gdy do odwołania do bazy danych są dodawane nowe dane. Nie musisz pisać dodatkowego kodu, aby to się stało.

W Javie i Node.js funkcja wywołania zwrotnego otrzymuje DataSnapshot, który jest migawką danych. Zrzut to obraz danych z konkretnego odwołania do bazy danych w określonym momencie. Wywołanie val() / getValue() na zrzucie ekranu zwraca obiektową reprezentację danych w danym języku. Jeśli w lokalizacji odniesienia nie ma żadnych danych, wartość migawkowego raportu to null. Metoda get() w Pythonie zwraca dane bezpośrednio w postaci kodu Pythona. Funkcja Get() w Go deserializuje dane do określonej struktury danych.

Zwróć uwagę, że w powyższym przykładzie użyliśmy typu zdarzenia value, który odczytuje całą zawartość odniesienia do bazy danych Firebase, nawet jeśli zmienił się tylko jeden fragment danych. value to jeden z 5 typów zdarzeń wymienionych poniżej, których możesz używać do odczytu danych z bazy danych.

Odczytywanie typów zdarzeń w Javie i Node.js

Wartość

Zdarzenie value służy do odczytania statycznego zrzutu ekranu zawartości w danej ścieżce bazy danych w stanie, w jakim była ona w momencie zdarzenia odczytu. Jest ona wywoływana raz z pierwotnymi danymi i ponownie za każdym razem, gdy dane ulegną zmianie. Do funkcji wywołania zwrotnego zdarzenia przekazywany jest zrzut zawierający wszystkie dane w danej lokalizacji, w tym dane podrzędne. W tym przykładzie kodu funkcja value zwraca wszystkie posty na blogu w aplikacji. Za każdym razem, gdy dodasz nowy post, funkcja wywołania zwrotnego zwróci wszystkie posty.

Dodano element podrzędny

Zdarzenie child_added jest zwykle używane do pobierania listy elementów z bazy danych. W odróżnieniu od metody value, która zwraca całą zawartość lokalizacji, parametr child_added jest wywoływany raz dla każdego istniejącego elementu podrzędnego, a następnie za każdym razem, gdy do określonej ścieżki zostanie dodany nowy element podrzędny. Do funkcji wywołania zwrotnego zdarzenia przekazywany jest snapshot zawierający dane nowego podrzędnego. Na potrzeby sortowania jest też przekazywany drugi argument zawierający klucz poprzedniego podrzędnego.

Jeśli chcesz pobierać tylko dane o każdym nowym poście dodanym do Twojej aplikacji do blogowania, możesz użyć 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);
});

W tym przykładzie zrzut ekranu będzie zawierać obiekt z pojedynczym postem na blogu. Ponieważ pakiet SDK konwertuje posty na obiekty, pobierając wartość, masz dostęp do właściwości autora i tytułu postu, wywołując odpowiednio author i title. Masz też dostęp do identyfikatora poprzedniego posta z drugiego argumentu prevChildKey.

Zmieniono element podrzędny

Zdarzenie child_changed jest wywoływane za każdym razem, gdy węzeł podrzędny zostanie zmodyfikowany. Obejmuje to wszelkie modyfikacje potomków węzła podrzędnego. Jest ona zwykle używana w połączeniu z elementami child_addedchild_removed, aby reagować na zmiany w liście elementów. Zrzut przekazany do wywołania zwrotnego zdarzenia zawiera zaktualizowane dane elementu podrzędnego.

Aby odczytać zaktualizowane dane w postach na blogu po ich edytowaniu, możesz użyć 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

Zdarzenie child_removed jest wywoływane, gdy usuwasz bezpośrednie dziecko. Zwykle jest używany w połączeniu z atrybutami child_added i child_changed. Zrzut podany do wywołania zdarzenia zawiera dane usuniętego podrzędnego.

W przypadku bloga możesz użyć child_removed, aby wysłać do konsoli powiadomienie o usuniętym poście:

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

Zdarzenie child_moved jest używane podczas pracy z uporządkowanymi danymi. Zostało to opisane w następnej sekcji.

Gwarancje dotyczące wydarzeń

Baza danych Firebase zapewnia kilka ważnych gwarancji dotyczących zdarzeń:

Gwarancje zdarzeń bazy danych
Zdarzenia będą zawsze wywoływane, gdy zmieni się stan lokalny.
Zdarzenia zawsze odzwierciedlają prawidłowy stan danych, nawet jeśli operacje lokalne lub czasowanie powodują tymczasowe różnice, np. tymczasową utratę połączenia z siecią.
Zapisy z pojedynczego klienta są zawsze zapisywane na serwerze i przesyłane do innych użytkowników w kolejności.
Zdarzenia wartości są zawsze wyzwalane jako ostatnie i gwarantują uwzględnienie aktualizacji ze wszystkich innych zdarzeń, które wystąpiły przed wykonaniem zrzutu.

Ponieważ zdarzenia wartości są zawsze wywoływane jako ostatnie, ten przykład będzie zawsze działał:

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);
});

Odłączanie wywołań zwrotnych

Wywołania zwrotne są usuwane przez określenie typu zdarzenia i funkcji wywołania zwrotnego do usunięcia:

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

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

Jeśli przekazujesz parametr on() w kontekście zakresu, musisz go przekazać podczas odłączania wywołania zwrotnego:

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

Jeśli chcesz usunąć wszystkie wywołania zwrotne w danej lokalizacji, wykonaj te czynności:

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();

Jednorazowy odczyt danych

W niektórych przypadkach może być przydatne, aby połączenie zwrotne zostało wykonane raz, a następnie natychmiast usunięte. Aby ułatwić Ci to zadanie, opracowaliśmy funkcję pomocniczą:

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)
}

Wysyłanie zapytań o dane

Zapytania do bazy danych Firebase pozwalają selektywnie pobierać dane na podstawie różnych czynników. Aby utworzyć zapytanie w bazie danych, musisz najpierw określić sposób sortowania danych za pomocą jednej z funkcji sortowania: orderByChild(), orderByKey() lub orderByValue(). Następnie możesz je łączyć z 5 innymi metodami, aby przeprowadzać złożone zapytania: limitToFirst(), limitToLast(), startAt(), endAt()equalTo().

Ponieważ wszyscy w Firebase uważamy dinozaury za fajne, użyjemy fragmentu z przykładowej bazy danych zawierającej informacje o dinosaurach, aby pokazać, jak możesz wysyłać zapytania o dane do bazy danych Firebase:

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

Dane można sortować na 3 sposoby: według klucza podrzędnego, klucza lub wartości. Podstawowe zapytanie do bazy danych zaczyna się od jednej z tych funkcji sortowania, które są opisane poniżej.

sortowanie według określonego klucza podrzędnego;

Możesz sortować węzły według wspólnego klucza podrzędnego, przekazując ten klucz do funkcji orderByChild(). Aby na przykład odczytać wszystkie dinozaury posortowane według wysokości, wykonaj te czynności:

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)
}

Każdy węzeł, który nie ma klucza podrzędnego, na podstawie którego wysyłamy zapytanie, jest sortowany z wartością null, co oznacza, że będzie pierwszy w kolejności. Szczegółowe informacje o tym, jak są sortowane dane, znajdziesz w sekcji Sortowanie danych.

Zapytania można też sortować według elementów podrzędnych ukrytych głęboko w hierarchii, a nie tylko elementów podrzędnych znajdujących się na 1 poziomie. Jest to przydatne, jeśli masz głęboko zagnieżdżone dane, np. takie:

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

Aby teraz zapytać o wysokość, możesz użyć pełnej ścieżki do obiektu zamiast pojedynczego klucza:

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)
}

Zapytania mogą być sortowane według tylko jednego klucza naraz. Wywołanie funkcji orderByChild() kilka razy w przypadku tego samego zapytania powoduje błąd.

Sortowanie według klucza

Możesz też sortować węzły według kluczy za pomocą metody orderByKey(). W tym przykładzie wszystkie dinozaury są odczytywane w kolejności alfabetycznej:

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)

Sortowanie według wartości

Korzystając z metody orderByValue(), możesz sortować węzły według wartości ich kluczy podrzędnych. Załóżmy, że dinozaury biorą udział w zawodach sportowych, a Ty śledzisz ich wyniki w takim formacie:

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

Aby posortować dinozaury według ich wyniku, możesz utworzyć takie zapytanie:

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)
}

Więcej informacji o sortowaniu wartości null, wartości logicznych, ciągów tekstowych i wartości obiektów podczas używania funkcji orderByValue() znajdziesz w sekcji Uporządkowanie danych.

Zapytania złożone

Teraz, gdy już wiesz, jak są uporządkowane Twoje dane, możesz tworzyć bardziej złożone zapytania, korzystając z opisanych niżej metod limitrange.

Ogranicz zapytania

Zapytania limitToFirst() i limitToLast() służą do ustawiania maksymalnej liczby elementów podrzędnych, które mają być synchronizowane w przypadku danego wywołania zwrotnego. Jeśli ustawisz limit na 100, początkowo otrzymasz tylko maksymalnie 100 zdarzeń child_added. Jeśli w bazie danych jest mniej niż 100 wiadomości, dla każdej wiadomości będzie wywoływane zdarzenie child_added. Jeśli jednak masz ich ponad 100, otrzymasz zdarzenie child_addedtylko dla 100 z nich. Jeśli używasz funkcji limitToFirst(), są to pierwsze 100 uporządkowanych wiadomości, a jeśli limitToLast() – ostatnie 100 uporządkowanych wiadomości. Gdy elementy się zmieniają, otrzymujesz zdarzenia child_added dotyczące elementów, które wchodzą do zapytania, oraz zdarzenia child_removed dotyczące elementów, które je opuszczają. Dzięki temu łączna liczba pozostaje równa 100.

Korzystając z bazy danych o dinosaurach i orderByChild(), możesz znaleźć 2 najcięższe dinozaury:

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())
}

Funkcja child_added jest wywoływana dokładnie 2 razy, chyba że w bazie danych jest mniej niż 2 dinozaury. Zostanie też wywoływana za każdym razem, gdy do bazy danych zostanie dodany nowy, cięższy dinozaur. W Pythonie zapytanie zwraca bezpośrednio OrderedDict zawierające 2 najcięższe dinozaury.

Podobnie możesz znaleźć 2 najkrótsze dinozaury, używając zapytania 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);
});
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())
}

Funkcja child_added jest wywoływana dokładnie 2 razy, chyba że w bazie danych jest mniej niż 2 dinozaury. Zostanie ona też wywołana ponownie, jeśli jeden z pierwszych dwóch dinozaurów zostanie usunięty z bazy danych, ponieważ nowy dinozaur będzie drugim najkrótszym. W Pythonie zapytanie bezpośrednio zwraca wartość OrderedDict zawierającą najkrótsze dinozaury.

Możesz też wykonywać zapytania o limity za pomocą funkcji orderByValue(). Jeśli chcesz utworzyć tabelę wyników z 3 dinosaurami sportowymi dinozaurów o najwyższej liczbie punktów, wykonaj te czynności:

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)
}

Zapytania dotyczące zakresu

Korzystając z parametrów startAt(), endAt()equalTo(), możesz wybierać dowolne punkty początkowe i końcowe zapytań. Jeśli na przykład chcesz znaleźć wszystkie dinozaury mające co najmniej 3 metry wysokości, możesz połączyć elementy orderByChild() i 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())
}

Za pomocą endAt() możesz znaleźć wszystkie dinozaury, których nazwy występują w alfabecie przed nazwą 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);
});
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())
}

Możesz połączyć operatory startAt() i endAt(), aby ograniczyć oba końce zapytania. W tym przykładzie zostaną znalezione wszystkie dinozaury, których nazwa zaczyna się od litery „b”:

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())
}

Metoda equalTo() umożliwia filtrowanie na podstawie dopasowań ścisłych. Podobnie jak w przypadku innych zapytań dotyczących zakresu, zostanie ono wykonane w przypadku każdego pasującego węzła podrzędnego. Za pomocą tego zapytania możesz na przykład znaleźć wszystkie dinozaury o wysokości 25 metrów:

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())
}

Zapytania zakresowe są też przydatne, gdy chcesz podzielić dane na strony.

Łączę wszystko w całość

Możesz łączyć te techniki, aby tworzyć złożone zapytania. Możesz na przykład znaleźć nazwę dinozaura, który jest tylko krótszy od stegozaura:

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")
}

W jaki sposób dane są porządkowane

W tej sekcji wyjaśniamy, jak są sortowane dane przy użyciu każdej z 4 funkcji sortowania.

Kolejność według elementu podrzędnego

Gdy używasz orderByChild(), dane zawierające określony klucz podrzędny są uporządkowane w taki sposób:

  1. Najpierw są wybierane elementy podrzędne, dla których wartość atrybutu podrzędnego to null.
  2. Następnie podawane są elementy podrzędne o wartości false dla określonego klucza podrzędnego. Jeśli wiele elementów podrzędnych ma wartość false, są one sortowane alfabetycznie według klucza.
  3. Następnie podawane są elementy podrzędne o wartości true dla określonego klucza podrzędnego. Jeśli wiele elementów podrzędnych ma wartość true, są one sortowane alfabetycznie według klucza.
  4. Następne są elementy podrzędne z wartością liczbową, posortowane w kolejności rosnącej. Jeśli wiele elementów podrzędnych ma tę samą wartość numeryczną w wybranym węźle podrzędnym, są one sortowane według klucza.
  5. Ciągi znaków znajdują się po liczbach i są sortowane leksykograficznie w kolejności rosnącej. Jeśli wiele elementów podrzędnych ma tę samą wartość w określonym węźle podrzędnym, są one uporządkowane leksykograficznie według klucza.
  6. Obiekty znajdują się na końcu i są posortowane leksykograficznie według klucza w kolejności rosnącej.

orderByKey

Gdy używasz parametru orderByKey() do sortowania danych, są one zwracane w kolejności rosnącej według klucza w taki sposób: Pamiętaj, że klucze mogą być tylko ciągami znaków.

  1. Najpierw wyświetlane są dzieci, których klucz można przeanalizować jako 32-bitową liczbę całkowitą, w kolejności rosnącej.
  2. Kolejne są elementy podrzędne z wartością ciągu znaków jako kluczem, posortowane leksykograficznie w kolejności rosnącej.

orderByValue

W przypadku użycia orderByValue() podrzędne elementy są sortowane według wartości. Kryteria sortowania są takie same jak w orderByChild(), z tym że zamiast wartości określonego klucza podrzędnego używana jest wartość węzła.