Z tego dokumentu dowiesz się, jak pobierać dane z bazy danych, jak są one uporządkowane i jak wykonywać proste zapytania dotyczące danych. Pobieranie danych w pakiecie Admin SDK jest nieco inaczej zaimplementowane w różnych językach programowania.
- Słuchacze asynchroniczni: dane przechowywane w Firebase Realtime Database są pobierane przez dołączenie słuchacza asynchronicznego do odwołania do bazy danych. Słuchacz jest wywoływany raz w przypadku początkowego stanu danych i ponownie za każdym razem, gdy dane się zmienią. Detektor zdarzeń może odbierać kilka różnych typów zdarzeń. Ten tryb pobierania danych jest obsługiwany w pakietach SDK administratora w językach Java, Node.js i Python.
- Blokowanie odczytu: dane przechowywane w Firebase Realtime Database są pobierane przez wywołanie metody blokującej na odwołaniu do bazy danych, która zwraca dane przechowywane w tym odwołaniu. Każde wywołanie metody jest operacją jednorazową. Oznacza to, że pakiet SDK nie rejestruje żadnych wywołań zwrotnych, które nasłuchują kolejnych aktualizacji danych. Ten model pobierania danych jest obsługiwany w pakietach SDK administratora w Pythonie i Go.
Pierwsze kroki
Aby dowiedzieć się, jak odczytywać dane z bazy danych Firebase, wróćmy do przykładu bloga z poprzedniego artykułu. Pamię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, w konsoli zobaczysz obiekt zawierający wszystkie Twoje posty. W przypadku Node.js i Javy funkcja nasłuchująca jest wywoływana za każdym razem, gdy do odwołania do bazy danych dodawane są nowe dane. Nie musisz pisać żadnego dodatkowego kodu, aby to osiągnąć.
W środowiskach Java i Node.js funkcja wywołania zwrotnego otrzymuje obiekt DataSnapshot
, który jest migawką danych. Zrzut to obraz danych w określonym momencie w czasie w odniesieniu do konkretnego miejsca w bazie danych. Wywołanie val()
/ getValue()
w przypadku zrzutu zwraca reprezentację danych w postaci obiektu w określonym języku. Jeśli w lokalizacji odniesienia nie ma danych, wartość migawki to null
. Metoda get()
w Pythonie zwraca bezpośrednio reprezentację danych w Pythonie. Funkcja Get()
w Go przekształca dane w określoną strukturę danych.
Zwróć uwagę, że w powyższym przykładzie użyliśmy typu zdarzenia value
, który odczytuje całą zawartość odwołania do bazy danych Firebase, nawet jeśli zmienił się tylko jeden element danych. value
to jeden z 5 różnych typów zdarzeń wymienionych poniżej, których możesz używać do odczytywania danych z bazy danych.
Odczytywanie typów zdarzeń w językach Java i Node.js
Wartość
Zdarzenie value
służy do odczytywania statycznej migawki zawartości w danej ścieżce bazy danych w momencie wystąpienia zdarzenia odczytu. Jest wywoływana raz z danymi początkowymi i ponownie za każdym razem, gdy dane się zmienią. Funkcja zwrotna zdarzenia otrzymuje zrzut zawierający wszystkie dane w tej lokalizacji, w tym dane podrzędne. W przykładzie kodu powyżej funkcja value
zwróciła wszystkie posty na blogu w aplikacji. Za każdym razem, gdy dodasz nowy post na blogu, funkcja wywołania zwrotnego zwróci wszystkie posty.
Dodano element podrzędny
Zdarzenie child_added
jest zwykle używane podczas pobierania listy produktów z bazy danych. W przeciwieństwie do funkcji value
, która zwraca całą zawartość lokalizacji, funkcja child_added
jest wywoływana 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. Funkcja zwrotna zdarzenia otrzymuje migawkę zawierającą dane nowego elementu podrzędnego. W celu określenia kolejności przekazywany jest też drugi argument zawierający klucz poprzedniego elementu podrzędnego.
Jeśli chcesz pobierać tylko dane dotyczące każdego nowego posta dodanego do aplikacji do blogowania, możesz użyć tego kodu: 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 migawka będzie zawierać obiekt z pojedynczym postem na blogu. Pakiet SDK przekształca posty w obiekty, pobierając ich wartość, więc 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
.
Zmiana elementu podrzędnego
Zdarzenie child_changed
jest wywoływane za każdym razem, gdy węzeł podrzędny zostanie zmodyfikowany. Dotyczy to wszelkich zmian w elementach podrzędnych węzła podrzędnego. Zazwyczaj jest używana w połączeniu z child_added
i child_removed
, aby reagować na zmiany na liście elementów. Zrzut przekazywany do wywołania zwrotnego zdarzenia zawiera zaktualizowane dane dotyczące elementu podrzędnego.
Możesz użyć child_changed
, aby odczytać zaktualizowane dane w postach na blogu po ich edycji:
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 usunięte
Zdarzenie child_removed
jest wywoływane, gdy zostanie usunięte bezpośrednie dziecko. Zwykle jest używana w połączeniu z właściwościami child_added
i child_changed
. Migawka przekazywana do wywołania zwrotnego zdarzenia zawiera dane usuniętego elementu podrzędnego.
W przykładzie bloga możesz użyć child_removed
, aby zalogować 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'); });
Dziecko zostało przeniesione
Zdarzenie child_moved
jest używane podczas pracy z uporządkowanymi danymi, co zostało opisane w następnej sekcji.
Gwarancje dotyczące wydarzeń
Baza danych Firebase daje kilka ważnych gwarancji dotyczących zdarzeń:
Gwarancje dotyczące zdarzeń w bazie danych |
---|
Zdarzenia będą zawsze wywoływane, gdy zmieni się stan lokalny. |
Wydarzenia zawsze będą odzwierciedlać prawidłowy stan danych, nawet w przypadkach, gdy operacje lokalne lub czas powodują tymczasowe różnice, np. w przypadku tymczasowej utraty połączenia z siecią. |
Zapisy z jednego klienta będą zawsze zapisywane na serwerze i rozsyłane do innych użytkowników w odpowiedniej kolejności. |
Zdarzenia wartości są zawsze wywoływane jako ostatnie i zawsze zawierają aktualizacje z innych zdarzeń, które wystąpiły przed wykonaniem tego zrzutu. |
Ponieważ zdarzenia wartości są zawsze wywoływane na końcu, poniższy 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 podanie typu zdarzenia i funkcji wywołania zwrotnego, która ma zostać usunięta, np. w ten sposób:
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()
przekazano kontekst zakresu, musi on zostać przekazany podczas odłączania wywołania zwrotnego:
Java
// Not applicable for Java
Node.js
ref.off('value', originalCallback, ctx);
Jeśli chcesz usunąć wszystkie prośby o oddzwonienie w 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();
Odczytywanie danych tylko raz
W niektórych przypadkach przydatne może być jednokrotne wywołanie funkcji zwrotnej, a następnie jej natychmiastowe usunięcie. Aby ułatwić Ci to zadanie, utworzyliś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ń dotyczących danych
Za pomocą zapytań do bazy danych Firebase możesz selektywnie pobierać dane na podstawie różnych czynników. Aby utworzyć zapytanie w bazie danych, zacznij od określenia sposobu sortowania danych za pomocą jednej z funkcji sortowania: orderByChild()
, orderByKey()
lub orderByValue()
. Możesz je połączyć z 5 innymi metodami, aby przeprowadzać złożone zapytania: limitToFirst()
, limitToLast()
, startAt()
, endAt()
i equalTo()
.
Wszyscy w Firebase uważamy, że dinozaury są fajne, więc użyjemy fragmentu przykładowej bazy danych z faktami o dinozaurach, aby pokazać, jak można wysyłać zapytania do danych w bazie danych Firebase:
{ "lambeosaurus": { "height" : 2.1, "length" : 12.5, "weight": 5000 }, "stegosaurus": { "height" : 4, "length" : 9, "weight" : 2500 } }
Dane możesz uporządkować na 3 sposoby: według klucza podrzędnego, klucza lub wartości. Podstawowe zapytanie do bazy danych zaczyna się od jednej z tych funkcji porządkowania, z których każda jest opisana poniżej.
Sortowanie według określonego klucza podrzędnego
Węzły możesz uporządkować 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 wysokości, 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(f'{key} was {val} meters tall')
Go
// Dinosaur is a json-serializable type. type Dinosaur struct { Height int `json:"height"` Width int `json:"width"` } ref := client.NewRef("dinosaurs") results, err := ref.OrderByChild("height").GetOrdered(ctx) if err != nil { log.Fatalln("Error querying database:", err) } for _, r := range results { var d Dinosaur if err := r.Unmarshal(&d); err != nil { log.Fatalln("Error unmarshaling result:", err) } fmt.Printf("%s was %d meteres tall", r.Key(), d.Height) }
Każdy węzeł, który nie ma klucza podrzędnego, o który pytamy, jest sortowany z wartością null
, co oznacza, że będzie pierwszy w kolejności. Szczegółowe informacje o kolejności danych znajdziesz w sekcji Kolejność danych.
Zapytania można też porządkować według głęboko zagnieżdżonych elementów podrzędnych, a nie tylko tych na niższym poziomie. Jest to przydatne, jeśli masz głęboko zagnieżdżone dane, takie jak te:
{ "lambeosaurus": { "dimensions": { "height" : 2.1, "length" : 12.5, "weight": 5000 } }, "stegosaurus": { "dimensions": { "height" : 4, "length" : 9, "weight" : 2500 } } }
Aby teraz wysłać 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(f'{key} was {val} meters tall')
Go
ref := client.NewRef("dinosaurs") results, err := ref.OrderByChild("dimensions/height").GetOrdered(ctx) if err != nil { log.Fatalln("Error querying database:", err) } for _, r := range results { var d Dinosaur if err := r.Unmarshal(&d); err != nil { log.Fatalln("Error unmarshaling result:", err) } fmt.Printf("%s was %d meteres tall", r.Key(), d.Height) }
Zapytania mogą być sortowane tylko według jednego klucza naraz. Wielokrotne wywoływanie funkcji orderByChild()
w przypadku tego samego zapytania powoduje błąd.
Sortowanie według klucza
Węzły możesz też uporządkować według kluczy za pomocą metody orderByKey()
. W przykładzie poniżej odczytujemy wszystkie dinozaury w porządku alfabetycznym:
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
Węzły możesz uporządkować według wartości kluczy podrzędnych za pomocą metody orderByValue()
. Załóżmy, że dinozaury biorą udział w zawodach sportowych, a Ty śledzisz ich wyniki w tym formacie:
{ "scores": { "bruhathkayosaurus" : 55, "lambeosaurus" : 21, "linhenykus" : 80, "pterodactyl" : 93, "stegosaurus" : 5, "triceratops" : 22 } }
Aby posortować dinozaury według wyniku, możesz utworzyć to 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(f'The {key} dinosaur\'s score is {val}')
Go
ref := client.NewRef("scores") results, err := ref.OrderByValue().GetOrdered(ctx) if err != nil { log.Fatalln("Error querying database:", err) } for _, r := range results { var score int if err := r.Unmarshal(&score); err != nil { log.Fatalln("Error unmarshaling result:", err) } fmt.Printf("The %s dinosaur's score is %d\n", r.Key(), score) }
W sekcji Jak są uporządkowane dane znajdziesz wyjaśnienie, jak wartości null
, logiczne, ciągi tekstowe i obiekty są sortowane podczas używania orderByValue()
.
Złożone zapytania
Teraz, gdy wiesz już, jak są uporządkowane Twoje dane, możesz użyć opisanych poniżej metod limit lub range, aby tworzyć bardziej złożone zapytania.
Ograniczanie zapytań
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 100, początkowo otrzymasz tylko 100 zdarzeń child_added
. Jeśli w bazie danych masz mniej niż 100 wiadomości, dla każdej z nich zostanie wywołane zdarzenie child_added
. Jeśli jednak masz ponad 100 wiadomości, otrzymasz zdarzenie child_added
tylko w przypadku 100 z nich. Jest to pierwszych 100 uporządkowanych wiadomości, jeśli używasz
limitToFirst()
, lub ostatnich 100 uporządkowanych wiadomości, jeśli używasz
limitToLast()
. W miarę zmian w produktach będziesz otrzymywać child_added
zdarzenia dotyczące produktów, które spełniają kryteria zapytania, oraz child_removed
zdarzenia dotyczące produktów, które przestają spełniać te kryteria, dzięki czemu łączna liczba produktów pozostanie na poziomie 100.
Korzystając z bazy danych o dinozaurach 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()) }
Wywołanie zwrotne child_added
jest wywoływane dokładnie 2 razy, chyba że w bazie danych jest mniej niż 2 dinozaury. Będzie też uruchamiany za każdym razem, gdy do bazy danych zostanie dodany nowy, cięższy dinozaur.
W Pythonie zapytanie zwraca bezpośrednio OrderedDict
zawierającą 2 najcięższe dinozaury.
Podobnie możesz znaleźć 2 najkrótsze dinozaury, używając 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 jest mniej niż 2 dinozaury. Zostanie on też ponownie wywołany, jeśli jeden z pierwszych dwóch dinozaurów zostanie usunięty z bazy danych, ponieważ nowy dinozaur będzie teraz drugim najkrótszym. W Pythonie zapytanie bezpośrednio zwraca OrderedDict
zawierającą najkrótsze dinozaury.
Możesz też wykonywać zapytania o limity za pomocą orderByValue()
. Jeśli chcesz utworzyć tabelę wyników z 3 dinozaurami, które osiągnęły najlepsze wyniki w dino sporcie, możesz wykonać 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(f'The {key} dinosaur\'s score is {val}')
Go
ref := client.NewRef("scores") results, err := ref.OrderByValue().LimitToLast(3).GetOrdered(ctx) if err != nil { log.Fatalln("Error querying database:", err) } for _, r := range results { var score int if err := r.Unmarshal(&score); err != nil { log.Fatalln("Error unmarshaling result:", err) } fmt.Printf("The %s dinosaur's score is %d\n", r.Key(), score) }
Zapytania dotyczące zakresu
Użycie funkcji startAt()
, endAt()
i equalTo()
pozwala wybrać dowolne punkty początkowe i końcowe zapytań. Jeśli na przykład chcesz znaleźć wszystkie dinozaury o wysokości co najmniej 3 metrów, możesz połączyć 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()) }
Możesz użyć znaku endAt()
, aby znaleźć wszystkie dinozaury, których nazwy są leksykograficznie wcześniejsze niż 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ć startAt()
i endAt()
, aby ograniczyć obie strony zapytania. Poniższy przykład pozwala znaleźć 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('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ń zakresowych, to zapytanie zostanie uruchomione dla każdego pasującego węzła podrzędnego. Możesz na przykład użyć tego zapytania, aby 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 musisz podzielić dane na strony.
Podsumowanie
Możesz łączyć wszystkie te techniki, aby tworzyć złożone zapytania. Możesz na przykład znaleźć nazwę dinozaura, który jest nieco mniejszy 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(f'The dinosaur just shorter than the stegosaurus is {key}') return else: print('The stegosaurus is the shortest dino')
Go
ref := client.NewRef("dinosaurs") var favDinoHeight int if err := ref.Child("stegosaurus").Child("height").Get(ctx, &favDinoHeight); err != nil { log.Fatalln("Error querying database:", err) } query := ref.OrderByChild("height").EndAt(favDinoHeight).LimitToLast(2) results, err := query.GetOrdered(ctx) if err != nil { log.Fatalln("Error querying database:", err) } if len(results) == 2 { // Data is ordered by increasing height, so we want the first entry. // Second entry is stegosarus. fmt.Printf("The dinosaur just shorter than the stegosaurus is %s\n", results[0].Key()) } else { fmt.Println("The stegosaurus is the shortest dino") }
Sposób porządkowania danych
W tej sekcji wyjaśniamy, jak są uporządkowane dane podczas korzystania z każdej z 4 funkcji porządkowania.
orderByChild
Gdy używasz orderByChild()
, dane zawierające określony klucz podrzędny są uporządkowane w ten sposób:
- Najpierw pojawiają się dzieci z wartością
null
dla określonego klucza dziecka. - Następnie pojawią się elementy podrzędne z wartością
false
dla określonego klucza elementu podrzędnego. Jeśli wiele elementów podrzędnych ma wartośćfalse
, są one sortowane leksykograficznie według klucza. - Następnie pojawią się elementy podrzędne z wartością
true
dla określonego klucza elementu podrzędnego. Jeśli kilka elementów podrzędnych ma wartośćtrue
, są one sortowane leksykograficznie według klucza. - Następnie pojawiają się elementy podrzędne z wartością liczbową, posortowane w kolejności rosnącej. Jeśli kilka elementów podrzędnych ma tę samą wartość liczbową w przypadku określonego węzła podrzędnego, są one sortowane według klucza.
- Ciągi znaków występują po liczbach i są sortowane leksykograficznie w kolejności rosnącej. Jeśli kilka elementów podrzędnych ma tę samą wartość w przypadku określonego węzła podrzędnego, są one uporządkowane leksykograficznie według klucza.
- Obiekty są umieszczane na końcu i sortowane leksykograficznie według klucza w kolejności rosnącej.
orderByKey
Gdy używasz funkcji orderByKey()
do sortowania danych, są one zwracane w kolejności rosnącej według klucza w ten sposób: Pamiętaj, że klucze mogą być tylko ciągami znaków.
- Najpierw pojawiają się dzieci z kluczem, który można przeanalizować jako 32-bitową liczbę całkowitą, posortowane w kolejności rosnącej.
- Następnie pojawiają się dzieci z wartością tekstową jako kluczem, posortowane leksykograficznie w kolejności rosnącej.
orderByValue
W przypadku korzystania z orderByValue()
dzieci są uporządkowane według wartości. Kryteria sortowania są takie same jak w przypadku orderByChild()
, z tym że zamiast wartości określonego klucza podrzędnego używana jest wartość węzła.