Pobieram dane

Ten dokument zawiera podstawowe informacje o pobieraniu danych z baz danych, ich kolejności oraz przeprowadzaniu prostych zapytań o dane. Pobieranie danych w pakiecie Admin SDK działa nieco inaczej w zależności od języka programowania.

  1. Detektory asynchroniczne: dane przechowywane w Bazie danych czasu rzeczywistego Firebase są pobierane przez dołączenie odbiornika asynchronicznego do bazy danych. Detektor jest aktywowany raz dla początkowego stanu danych i ponownie przy każdej zmianie danych. Detektor zdarzeń może otrzymywać kilka różnych typów zdarzeń. Ten tryb pobierania danych jest obsługiwany w pakietach SDK Java, Node.js i Python Admin.
  2. Blokowanie odczytów: dane przechowywane w Bazie danych czasu rzeczywistego Firebase 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 pakiet SDK nie rejestruje żadnych wywołań zwrotnych, które wsłuchują się w kolejne aktualizacje danych. Ten model pobierania danych jest obsługiwany w pakietach SDK Python i Go Admin.

Pierwsze kroki

Wróćmy do przykładu z blogiem z poprzedniego artykułu, aby dowiedzieć się, jak odczytywać dane z bazy danych Firebase. Zapamiętaj, ż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)
}

Jeśli uruchomisz powyższy kod, zobaczysz obiekt zawierający wszystkie Twoje posty zarejestrowane w konsoli. W środowiskach Node.js i Javie funkcja detektora jest wywoływana za każdym razem, gdy do odniesienia do bazy danych zostaną dodane nowe dane. Aby tak się stało, nie trzeba pisać żadnego dodatkowego kodu.

W Javie i Node.js funkcja wywołania zwrotnego otrzymuje DataSnapshot, który jest migawką danych. Zrzut to obraz danych w konkretnym odniesieniu do bazy danych z określonego punktu w czasie. Wywołanie val() / getValue() na zrzucie ekranu zwraca obiektową reprezentację danych w danym języku. Jeśli w lokalizacji pliku referencyjnego nie ma żadnych danych, zrzut ma wartość null. Metoda get() w Pythonie zwraca bezpośrednio reprezentację danych w języku Python. Funkcja Get() w Go pozwala przekształcać dane w określoną strukturę.

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 odczytu statycznego zrzutu zawartości w danej ścieżce bazy danych, która istniała w momencie zdarzenia odczytu. Jest ona aktywowana raz z danymi początkowymi i ponownie przy każdej zmianie danych. Wywołanie zwrotne zdarzenia jest przekazywane do zrzutu zawierającego wszystkie dane w danej lokalizacji, w tym dane podrzędne. W powyższym przykładzie kodu funkcja value zwróciła wszystkie posty na blogu w Twojej aplikacji. Funkcja wywołania zwrotnego zwraca wszystkie te posty po każdym dodaniu nowego posta na blogu.

Dodano dziecko

Zdarzenie child_added jest zwykle używane przy pobieraniu 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. Wywołanie zwrotne zdarzenia jest przekazywane do zrzutu zawierającego dane nowego elementu podrzędnego. Na potrzeby porządkowania przekazujemy też drugi argument zawierający klucz poprzedniego elementu 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 będzie zawierać obiekt z pojedynczym postem na blogu. Pakiet SDK konwertuje posty na obiekty, pobierając wartość, dlatego masz dostęp do właściwości autora i tytułu posta, wywołując odpowiednio author i title. Masz też dostęp do poprzedniego identyfikatora posta z drugiego argumentu prevChildKey.

Zmieniono element podrzędny

Zdarzenie child_changed jest wywoływane za każdym razem, gdy zmodyfikowany jest węzeł podrzędny. Obejmuje to wszelkie modyfikacje elementów podrzędnych węzła podrzędnego. Zwykle jest używany w połączeniu z atrybutami child_added i child_removed w odpowiedzi na zmiany na liście elementów. Zrzut przekazany do wywołania zwrotnego zdarzenia zawiera zaktualizowane dane elementu podrzędnego.

Za pomocą usługi child_changed możesz odczytywać zaktualizowane dane postów na blogu, gdy są one edytowane:

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

Dziecko zostało usunięte

Zdarzenie child_removed jest wyzwalane po usunięciu natychmiastowego elementu podrzędnego. Zwykle jest używany w połączeniu z atrybutami child_added i child_changed. Zrzut przekazany do wywołania zwrotnego zdarzenia zawiera dane usuniętego elementu podrzędnego.

W przykładowym na blogu możesz użyć pola child_removed, aby zapisać w 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');
});

Przeniesiono dziecko

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

Gwarancje zdarzeń

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

Gwarancje zdarzeń bazy danych
Zdarzenia będą zawsze wywoływane po zmianie stanu lokalnego.
Zdarzenia zawsze będą odzwierciedlać prawidłowy stan danych, nawet jeśli operacje lokalne lub czasy powodują tymczasowe różnice, np. w przypadku tymczasowej utraty połączenia sieciowego.
Zapisy z pojedynczego klienta zawsze będą zapisywane na serwerze i przesyłane do innych użytkowników w odpowiedniej kolejności.
Zdarzenia wartości są zawsze wywoływane 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 zawsze będzie 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 do funkcji on() został przekazany kontekst zakresu, musi on być przekazywany przy odłączaniu wywołania zwrotnego:

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

Jeśli chcesz usunąć wszystkie wywołania zwrotne z danej lokalizacji, możesz wykonać 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 warto użyć wywołania zwrotnego, które należy wywołać od razu, a potem od razu je usunąć. Aby to ułatwić, 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, zacznij od określenia kolejności danych za pomocą jednej z funkcji porządkowania: orderByChild(), orderByKey() lub orderByValue(). Następnie możesz połączyć te metody z 5 innymi metodami, aby wykonywać złożone zapytania: limitToFirst(), limitToLast(), startAt(), endAt() i equalTo().

Ponieważ wszyscy w Firebase uważamy, że dinozaury są całkiem fajne, użyjemy fragmentu z przykładowej bazy danych faktów o dinozaurach, aby zademonstrować, jak można wykonywać zapytania o dane w bazie danych Firebase.

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

Dane możesz sortować na 3 sposoby: według klucza podrzędnego, według klucza lub według wartości. Podstawowe zapytanie do bazy danych rozpoczyna się od jednej z tych funkcji porządkowania. Każda z nich została objaśniona 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 uporządkowane według wzrostu, możesz wykonać 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ł bez klucza podrzędnego, którego dotyczy zapytanie, jest posortowany od wartości null, co oznacza, że jest na pierwszym miejscu w kolejności. Szczegółowe informacje o kolejności danych znajdziesz w sekcji Sposób porządkowania danych.

Zapytania mogą być też porządkowane według głęboko zagnieżdżonych elementów podrzędnych, a nie tylko według elementów podrzędnych. Jest to przydatne, jeśli masz głęboko zagnieżdżone dane:

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

Aby przesłać teraz zapytanie 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 tylko według 1 klucza naraz. Wielokrotne wywołanie metody orderByChild() dla tego samego zapytania powoduje błąd.

Sortowanie według klucza

Możesz też sortować węzły według ich kluczy, korzystając z metody orderByKey(). W tym przykładzie podano wszystkie dinozaury 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 dinozaurów, a ich wyniki zapisujesz 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ć następujące 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)
}

W sekcji Sposób sortowania danych dowiesz się, jak są sortowane wartości null, wartości logiczne, ciągi znaków i obiekty podczas korzystania z orderByValue().

Złożone zapytania

Skoro kolejność wyświetlania danych jest już jasna, możesz przejść do bardziej złożonych zapytań, korzystając z opisanych poniżej metod limitu i zakresu.

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 maksymalnie 100 wydarzeń 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 więcej niż 100 wiadomości, otrzymasz zdarzenie child_added tylko dla 100 z nich. To jest pierwsze 100 pierwszych wiadomości posortowanych, jeśli używasz limitToFirst(), lub 100 ostatnich, jeśli używasz limitToLast(). Gdy elementy się zmieniają, będziesz otrzymywać zdarzenia child_added dla elementów, które wpiszą w zapytaniu, i child_removed zdarzeń dla elementów, które je opuszczają, aby łączna liczba wynosiła 100.

Korzystając z bazy danych o dinozaurach i funkcji 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())
}

Wywołanie zwrotne child_added jest wywoływane dokładnie 2 razy, chyba że w bazie danych są przechowywane mniej niż 2 dinozaury. Będzie też uruchamiany za każdym razem, gdy zostanie dodany do bazy danych nowy, cięższy dinozaur. W Pythonie zapytanie bezpośrednio zwraca wartość OrderedDict zawierającą 2 najcięższe dinozaury.

Dwa najkrótsze dinozaury można też znaleźć za pomocą funkcji 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())
}

Wywołanie zwrotne child_added jest wywoływane dokładnie 2 razy, chyba że w bazie danych są przechowywane mniej niż 2 dinozaury. Zostanie również wystrzelony ponownie, gdy jeden z 2 pierwszych 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.

Zapytania dotyczące limitów możesz również wykonywać za pomocą funkcji orderByValue(). Jeśli chcesz utworzyć tabelę wyników z 3 najlepszymi dinozaurami sportowymi, 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 zakresów

startAt(), endAt() i equalTo() pozwalają wybrać dowolny punkt początkowy i końcowy 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())
}

Używając metody endAt(), możesz znaleźć wszystkie dinozaury, których nazwy pochodzą z perspektywy pterodaktyli leksykograficznej:

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ć startAt() i endAt(), by ograniczyć oba końce zapytania. Ten przykład pozwala znaleźć wszystkie dinozaury, których nazwy zaczynają 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. Tak jak w przypadku innych zapytań dotyczących zakresu, będzie się uruchamiać dla każdego pasującego węzła podrzędnego. Możesz na przykład użyć następującego zapytania, aby znaleźć wszystkie dinozaury mające 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 dotyczące zakresów są też przydatne przy dzieleniu danych na strony.

Łączę wszystko w całość

Możesz połączyć wszystkie te techniki, aby tworzyć złożone zapytania. Możesz na przykład znaleźć imię dinozaura krótszego niż stegozaur:

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 opisujemy, jak uporządkowane są dane w przypadku korzystania z każdej z tych 4 funkcji porządkowania.

Kolejność według elementu podrzędnego

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

  1. Elementy podrzędne z wartością null określonego klucza podrzędnego mają pierwszeństwo.
  2. Następne znajdują się w nim elementy podrzędne o wartości false określonego klucza podrzędnego. Jeśli kilka elementów podrzędnych ma wartość false, są one sortowane leksykograficznie według klucza.
  3. Następne znajdują się w nim elementy podrzędne o wartości true określonego klucza podrzędnego. Jeśli kilka elementów podrzędnych ma wartość true, są one sortowane leksykograficznie 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ść liczbową określonego węzła podrzędnego, 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 są na końcu i posortowane leksykograficznie według klucza w kolejności rosnącej.

Klucz zamówienia

Jeśli do sortowania danych używasz funkcji orderByKey(), dane są zwracane w kolejności rosnącej według klucza w podany niżej sposób. Pamiętaj, że klucze mogą być tylko ciągami znaków.

  1. Elementy podrzędne z kluczem, który można przeanalizować jako 32-bitową liczbę całkowitą, są na pierwszym miejscu, posortowane w kolejności rosnącej.
  2. Następnym elementem są elementy podrzędne z wartością w postaci ciągu znaków, posortowane leksykograficznie w kolejności rosnącej.

kolejność według wartości

Gdy używasz metody orderByValue(), elementy podrzędne są uporządkowane 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.