W tym dokumencie znajdziesz podstawowe informacje o pobieraniu danych z bazy danych, sposobie ich porządkowania oraz wykonywaniu prostych zapytań o dane. Pobieranie danych w pakiecie Admin SDK przebiega nieco inaczej w różnych językach programowania.
- Detektory asynchroniczne: dane przechowywane w Bazie danych czasu rzeczywistego Firebase są pobierane przez dołączenie asynchronicznego detektora do odwołania do bazy danych. Detektor jest uruchamiany 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 Admin SDK w języku Java, Node.js i Python Admin.
- Blokowanie odczytów: dane przechowywane w Bazie danych czasu rzeczywistego Firebase są pobierane przez wywołanie metody blokowania w odniesieniu do bazy danych, która zwraca dane przechowywane w pliku referencyjnym. Każde wywołanie metody jest operacją jednorazową. Oznacza to, że pakiet SDK nie rejestruje żadnych wywołań zwrotnych, które śledzą późniejsze zmiany danych. Ten model pobierania danych jest obsługiwany w pakietach Admin SDK w Pythonie i Go.
Pierwsze kroki
Przyjrzyjmy się ponownie przykładowi bloga z poprzedniego artykułu, aby dowiedzieć się, jak odczytywać dane z bazy danych Firebase. 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 postów, 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 przypadku środowiska Node.js i Jav funkcja detektora jest wywoływana za każdym razem, gdy do odwołania do bazy danych zostaną dodane nowe dane i nie trzeba pisać dodatkowego kodu.
W środowisku Java i Node.js funkcja wywołania zwrotnego odbiera element DataSnapshot
, który jest migawką danych. Migawka to obraz danych znajdujących się w określonej bazie danych w wybranym punkcie w czasie. Wywołanie val()
/ getValue()
w zrzucie zwraca reprezentację obiektu danych specyficzną dla języka. Jeśli w lokalizacji odwołania nie ma danych, wartość zrzutu wynosi null
. Metoda get()
w Pythonie bezpośrednio zwraca dane w Pythonie. Funkcja Get()
w Go rozdziela dane w konkretną 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 zmieniony został tylko jeden fragment danych. value
to jeden z 5 wymienionych poniżej typów zdarzeń, których możesz używać do odczytywania 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 w takiej postaci, w jakiej istniała w momencie wystąpienia zdarzenia odczytu. Jest wywoływany raz z danymi początkowymi i ponownie przy każdorazowej zmianie danych. Wywołanie zwrotne zdarzenia jest przekazywane migawkę zawierającą wszystkie dane w tej lokalizacji, w tym dane podrzędne. W powyższym przykładowym kodzie funkcja value
zwróciła wszystkie posty na blogu występujące w Twojej aplikacji. Za każdym razem, gdy dodawany jest nowy post na blogu, funkcja wywołania zwrotnego zwróci wszystkie posty.
Dodano dziecko
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, metoda child_added
jest wywoływana raz dla każdego istniejącego elementu podrzędnego, a potem ponownie za każdym razem, gdy do wskazanej ścieżki zostaje dodane nowe dziecko. Wywołanie zwrotne zdarzenia jest przekazywane migawkę zawierającą dane nowego elementu podrzędnego. W celu porządkowania jest też przekazywany drugi argument zawierający klucz poprzedniego elementu podrzędnego.
Jeśli chcesz pobrać tylko dane każdego nowego posta dodanego do aplikacji do blogowania, możesz użyć polecenia 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. SDK konwertuje posty na obiekty przez pobieranie wartości, więc masz dostęp do właściwości autora i tytułu posta, wywołując odpowiednio funkcje author
i title
. Masz też dostęp do poprzedniego identyfikatora posta dzięki 2 argumentowi prevChildKey
.
Konto podrzędne zostało zmienione
Zdarzenie child_changed
jest wywoływane po każdej zmianie węzła podrzędnego. Obejmuje to wszelkie zmiany w elementach podrzędnych węzła podrzędnego. Zwykle jest używany w połączeniu z atrybutami child_added
i child_removed
do reagowania na zmiany na liście elementów. Zrzut przekazany do wywołania zwrotnego zdarzenia zawiera zaktualizowane dane podmiotu podrzędnego.
Za pomocą child_changed
możesz czytać zaktualizowane dane w postach 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 wywoływane w przypadku natychmiastowego usunięcia konta 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 blogu możesz użyć funkcji child_removed
, aby zarejestrować 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, które opisujemy w następnej sekcji.
Gwarancje wydarzeń
Baza danych Firebase zapewnia kilka ważnych gwarancji dotyczących zdarzeń:
Gwarancje zdarzeń bazy danych |
---|
Zdarzenia będą wywoływane zawsze po zmianie stanu lokalnego. |
Zdarzenia zawsze będą odzwierciedlać prawidłowy stan danych, nawet w przypadkach, gdy operacje lokalne lub czasy powodują tymczasowe różnice, np. tymczasową utratę połączenia sieciowego. |
Zapisy z jednego klienta są zawsze zapisywane na serwerze i przekazywane do innych użytkowników w kolejności. |
Zdarzenia wartości są zawsze uruchamiane jako ostatnie i gwarantujemy, że będą zawierały aktualizacje wszystkich innych zdarzeń, które wystąpiły przed wykonaniem zrzutu. |
Zdarzenia wartości są zawsze uruchamiane jako ostatnie, więc 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, określając typ zdarzenia i funkcję wywołania zwrotnego, która ma zostać usunięta, jak w tym przykładzie:
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 przekazano kontekst zakresu do funkcji on()
, 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 wywołania zwrotne 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();
Jednorazowy odczyt danych
W niektórych przypadkach przydatne może być jednorazowe wywołanie zwrotne, a następnie natychmiastowe usunięcie. Aby to ułatwić, stworzyliś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) }
Wykonywanie zapytań dotyczących danych
Za pomocą zapytań do bazy danych Firebase możesz pobierać dane selektywnie na podstawie różnych czynników. Tworzenie zapytania do bazy danych rozpoczyna się od określenia sposobu sortowania danych przy użyciu jednej z funkcji porządkowania: orderByChild()
, orderByKey()
lub orderByValue()
. Możesz je połączyć z 5 innymi metodami wykonywania złożonych zapytań: limitToFirst()
, limitToLast()
, startAt()
, endAt()
i equalTo()
.
Wszyscy w Firebase uważają, że dinozaury są całkiem fajne, więc użyjemy fragmentu z przykładowej bazy danych z informacjami 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 uporządkować na 3 sposoby: według klucza podrzędnego, klucza lub wartości. Podstawowe zapytanie do bazy danych rozpoczyna się od jednej z tych funkcji porządkowania, które zostały opisane poniżej.
Kolejność według określonego klucza podrzędnego
Możesz uporządkować węzły według wspólnego klucza podrzędnego, przekazując ten klucz do 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ł, który nie ma klucza podrzędnego, dla którego wykonujemy zapytanie, jest posortowany z wartością null
, co oznacza, że będzie pierwszy w kolejności. Szczegółowe informacje o kolejności danych znajdziesz w sekcji na temat porządkowania danych.
Zapytania mogą być też uporządkowane według głęboko zagnieżdżonych elementów podrzędnych, a nie tylko elementów podrzędnych o jednym poziomie niżej. Jest to przydatne, jeśli masz głęboko zagnieżdżone dane, takie jak:
{ "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, a nie 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()
w tym samym zapytaniu powoduje błąd.
Kolejność według klucza
Możesz też uporządkować węzły według ich kluczy, korzystając z metody orderByKey()
. W tym przykładzie wszystkie dinozaury są zapisane 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
Możesz posortować węzły według wartości ich kluczy podrzędnych, korzystając z metody orderByValue()
. Załóżmy, że dinozaury odbywają zawody w sporcie dinozaurów, a ich wyniki śledzisz w takim formacie:
{ "scores": { "bruhathkayosaurus" : 55, "lambeosaurus" : 21, "linhenykus" : 80, "pterodactyl" : 93, "stegosaurus" : 5, "triceratops" : 22 } }
Aby posortować dinozaury według 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) }
W sekcji W jaki sposób dane są sortowane, aby dowiedzieć się, jak sortowane są wartości null
, wartości logiczne, ciągi znaków i obiekty w funkcji orderByValue()
,
Złożone zapytania
Skoro wiemy już, w jaki sposób uporządkowane są dane, możesz skorzystać z opisanych poniżej metod limitu i zakresu, by tworzyć bardziej złożone zapytania.
Ogranicz zapytania
Zapytania limitToFirst()
i limitToLast()
służą do ustawiania maksymalnej liczby elementów podrzędnych do synchronizacji w przypadku danego wywołania zwrotnego. Jeśli ustawisz limit na 100 zdarzeń, początkowo otrzymasz maksymalnie 100 zdarzeń child_added
. Jeśli w bazie danych jest mniej niż 100 wiadomości, dla każdej z nich będzie uruchamiane zdarzenie child_added
. Jeśli jednak masz ponad 100 wiadomości, otrzymasz tylko zdarzenie child_added
dla 100 z nich. To jest pierwsze 100 uporządkowanych wiadomości, jeśli używasz
limitToFirst()
, lub 100 ostatnich uporządkowanych wiadomości, jeśli używasz
limitToLast()
. W miarę zmiany będziesz otrzymywać zdarzenia child_added
dotyczące elementów, które wkraczają w zapytanie, i zdarzeń child_removed
w przypadku elementów, które je opuszczają. Dzięki temu łączna liczba będzie wynosić 100.
Korzystając z bazy danych o dinozaurach i funkcji orderByChild()
, możesz znaleźć 2 najcięższe:
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 uruchamiane dokładnie 2 razy, chyba że w bazie danych znajdują się mniej niż 2 dinozaury. Będzie też wystrzeliwany z każdego nowego, cięższego dinozaura, który zostanie dodany do bazy danych.
W Pythonie zapytanie bezpośrednio zwraca element OrderedDict
zawierający 2 najcięższe dinozaury.
Podobnie, aby znaleźć dwa najkrótsze dinozaury, użyj 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 znajdują się mniej niż 2 dinozaury. Uderz także ponownie, jeśli jeden z dwóch pierwszych dinozaurów zostanie usunięty z bazy, jako nowy będzie drugi najkrótszy. W Pythonie zapytanie bezpośrednio zwraca element OrderedDict
zawierający najkrótsze dinozaury.
Zapytania dotyczące limitów możesz też wykonywać za pomocą narzędzia orderByValue()
. Jeśli chcesz utworzyć tabelę wyników z 3 najbardziej punktowanymi dinozaurami w sporcie dinozaurów, 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('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
Używanie startAt()
, endAt()
i equalTo()
umożliwia wybór dowolnego punktu początkowego i końcowego dla zapytań. Jeśli na przykład chcesz znaleźć wszystkie dinozaury o wysokości co najmniej 3 metrów, 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()) }
Dzięki usłudze endAt()
możesz znaleźć wszystkie dinozaury, których imiona i nazwiska są wcześniejsze niż pterodaktyl leksykograficznie:
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ć atrybuty startAt()
i endAt()
, aby ograniczyć oba końce zapytania. Poniższy przykład pokazuje wszystkie dinozaury, których imiona rozpoczynają się na literę „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 on uruchamiany dla każdego pasującego węzła podrzędnego. Możesz np. wyszukać wszystkie dinozaury o wysokości 25 metrów, wpisując podane niżej zapytanie:
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ą również przydatne, gdy musisz podzielić dane na strony.
Łączę wszystko w całość
Możesz łączyć wszystkie te techniki, aby tworzyć złożone zapytania. Możesz na przykład znaleźć imię dinozaura, które jest tylko krótsze 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 są uporządkowane dane
W tej sekcji wyjaśniamy, jak są uporządkowane dane podczas korzystania z poszczególnych funkcji.
orderByChild
Gdy używana jest metoda orderByChild()
, dane zawierające określony klucz podrzędny są uporządkowane w ten sposób:
- Elementy podrzędne z wartością
null
w określonym kluczu podrzędnym są wyświetlane jako pierwsze. - Elementy podrzędne z wartością
false
w przypadku określonego klucza podrzędnego są następne. Jeśli wiele elementów podrzędnych ma wartośćfalse
, są one sortowane leksykograficznie według klucza. - Elementy podrzędne z wartością
true
w przypadku określonego klucza podrzędnego są następne. Jeśli wiele elementów podrzędnych ma wartośćtrue
, są one sortowane leksykograficznie według klucza. - Elementy podrzędne z wartościami liczbowymi są wyświetlane na następnej stronie, posortowane w kolejności rosnącej. Jeśli wiele elementów podrzędnych ma tę samą wartość liczbową w określonym węźle podrzędnym, są one sortowane według klucza.
- 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.
- Obiekty są na końcu i są posortowane leksykograficznie według klucza w kolejności rosnącej.
orderByKey
Gdy dane są sortowane za pomocą funkcji orderByKey()
, są one zwracane w kolejności rosnącej według klucza. Pamiętaj, że klucze mogą być tylko ciągami tekstowymi.
- Elementy podrzędne z kluczem, który można przeanalizować jako 32-bitową liczbę całkowitą, są posortowane w kolejności rosnącej.
- Elementy podrzędne z wartością w postaci ciągu znaków jako klucz, posortowane leksykograficznie w kolejności rosnącej.
wartość orderByValue
Gdy używana jest właściwość orderByValue()
, elementy podrzędne są sortowane według wartości. Kryteria kolejności są takie same jak w funkcji orderByChild()
, z tą różnicą, że zamiast wartości określonego klucza podrzędnego używana jest wartość węzła.