Dokument ten zawiera podstawowe informacje o pobieraniu danych z bazy danych, ich sortowaniu oraz wykonywaniu prostych zapytań. Pobieranie danych w pakiecie Admin SDK działa nieco inaczej w zależności od języka programowania.
- Asynchroniczne odbiorniki: dane przechowywane w elementach Firebase Realtime Database są pobierane przez dołączenie asynchronicznego odbiornika do odwołania do bazy danych. Listener jest wywoływany raz w przypadku początkowego stanu danych i ponownie za każdym razem, gdy dane ulegną zmianie. Detektor zdarzeń może otrzymywać kilka różnych typów zdarzeń. Ten tryb pobierania danych jest obsługiwany w pakietach SDK dla administratorów w językach Java, Node.js i Python.
- Blokowanie odczytów: dane przechowywane w Firebase Realtime Database są pobierane przez wywołanie metody blokującej w odniesieniu do bazy danych, która zwraca dane przechowywane w tym odniesieniu. Każde wywołanie metody jest operacją jednorazową. Oznacza to, że SDK nie rejestruje żadnych funkcji wywołania zwrotnego, które nasłuchują kolejnych aktualizacji danych. Ten model pobierania danych jest obsługiwany w pakietach SDK Python i Go Admin.
Pierwsze kroki
Aby dowiedzieć się, jak odczytywać dane z bazy danych Firebase, zwróćmy uwagę na przykład z blogowania z poprzedniego artykułu. Przypominamy, że posty na blogu w przykładowej aplikacji są przechowywane pod adresem URL bazy danych https://docs-examples.firebaseio.com/server/saving-data/fireblog/posts.json. Aby odczytać dane posta, możesz wykonać te czynności:
Java
public static class Post { public String author; public String title; public Post(String author, String title) { // ... } } // Get a reference to our posts final FirebaseDatabase database = FirebaseDatabase.getInstance(); DatabaseReference ref = database.getReference("server/saving-data/fireblog/posts"); // Attach a listener to read the data at our posts reference ref.addValueEventListener(new ValueEventListener() { @Override public void onDataChange(DataSnapshot dataSnapshot) { Post post = dataSnapshot.getValue(Post.class); System.out.println(post); } @Override public void onCancelled(DatabaseError databaseError) { System.out.println("The read failed: " + databaseError.getCode()); } });
Node.js
// Get a database reference to our posts const db = getDatabase(); const ref = db.ref('server/saving-data/fireblog/posts'); // Attach an asynchronous callback to read the data at our posts reference ref.on('value', (snapshot) => { console.log(snapshot.val()); }, (errorObject) => { console.log('The read failed: ' + errorObject.name); });
Python
# Import database module. from firebase_admin import db # Get a database reference to our posts ref = db.reference('server/saving-data/fireblog/posts') # Read the data at the posts reference (this is a blocking operation) print(ref.get())
Go
// Post is a json-serializable type. type Post struct { Author string `json:"author,omitempty"` Title string `json:"title,omitempty"` } // Create a database client from App. client, err := app.Database(ctx) if err != nil { log.Fatalln("Error initializing database client:", err) } // Get a database reference to our posts ref := client.NewRef("server/saving-data/fireblog/posts") // Read the data at the posts reference (this is a blocking operation) var post Post if err := ref.Get(ctx, &post); err != nil { log.Fatalln("Error reading value:", err) }
Po uruchomieniu tego kodu zobaczysz obiekt zawierający wszystkie posty zapisane w konsoli. W przypadku Node.js i Java funkcja listener jest wywoływana za każdym razem, gdy do odwołania do bazy danych są dodawane nowe dane. Nie musisz pisać dodatkowego kodu, aby to się stało.
W Javie i Node.js funkcja wywołania zwrotnego otrzymuje DataSnapshot
, który jest migawką danych. Zrzut to obraz danych z konkretnego odwołania do bazy danych w określonym momencie. Wywołanie val()
/ getValue()
na zrzucie ekranu zwraca obiektową reprezentację danych w danym języku. Jeśli w lokalizacji odniesienia nie ma żadnych danych, wartość migawkowego raportu to null
. Metoda get()
w Pythonie zwraca dane bezpośrednio w postaci kodu Pythona. Funkcja Get()
w Go deserializuje dane do określonej struktury danych.
Zwróć uwagę, że w powyższym przykładzie użyliśmy typu zdarzenia value
, który odczytuje całą zawartość odniesienia do bazy danych Firebase, nawet jeśli zmienił się tylko jeden fragment danych. value
to jeden z 5 typów zdarzeń wymienionych poniżej, których możesz używać do odczytu danych z bazy danych.
Odczytywanie typów zdarzeń w Javie i Node.js
Wartość
Zdarzenie value
służy do odczytania statycznego zrzutu ekranu zawartości w danej ścieżce bazy danych w stanie, w jakim była ona w momencie zdarzenia odczytu. Jest ona wywoływana raz z pierwotnymi danymi i ponownie za każdym razem, gdy dane ulegną zmianie. Do funkcji wywołania zwrotnego zdarzenia przekazywany jest zrzut zawierający wszystkie dane w danej lokalizacji, w tym dane podrzędne. W tym przykładzie kodu funkcja value
zwraca wszystkie posty na blogu w aplikacji. Za każdym razem, gdy dodasz nowy post, funkcja wywołania zwrotnego zwróci wszystkie posty.
Dodano element podrzędny
Zdarzenie child_added
jest zwykle używane do pobierania listy elementów z bazy danych. W odróżnieniu od metody value
, która zwraca całą zawartość lokalizacji, parametr child_added
jest wywoływany raz dla każdego istniejącego elementu podrzędnego, a następnie za każdym razem, gdy do określonej ścieżki zostanie dodany nowy element podrzędny. Do funkcji wywołania zwrotnego zdarzenia przekazywany jest snapshot zawierający dane nowego podrzędnego. Na potrzeby sortowania jest też przekazywany drugi argument zawierający klucz poprzedniego podrzędnego.
Jeśli chcesz pobierać tylko dane o każdym nowym poście dodanym do Twojej aplikacji do blogowania, możesz użyć child_added
:
Java
ref.addChildEventListener(new ChildEventListener() { @Override public void onChildAdded(DataSnapshot dataSnapshot, String prevChildKey) { Post newPost = dataSnapshot.getValue(Post.class); System.out.println("Author: " + newPost.author); System.out.println("Title: " + newPost.title); System.out.println("Previous Post ID: " + prevChildKey); } @Override public void onChildChanged(DataSnapshot dataSnapshot, String prevChildKey) {} @Override public void onChildRemoved(DataSnapshot dataSnapshot) {} @Override public void onChildMoved(DataSnapshot dataSnapshot, String prevChildKey) {} @Override public void onCancelled(DatabaseError databaseError) {} });
Node.js
// Retrieve new posts as they are added to our database ref.on('child_added', (snapshot, prevChildKey) => { const newPost = snapshot.val(); console.log('Author: ' + newPost.author); console.log('Title: ' + newPost.title); console.log('Previous Post ID: ' + prevChildKey); });
W tym przykładzie zrzut ekranu będzie zawierać obiekt z pojedynczym postem na blogu. Ponieważ pakiet SDK konwertuje posty na obiekty, pobierając wartość, masz dostęp do właściwości autora i tytułu postu, wywołując odpowiednio author
i title
. Masz też dostęp do identyfikatora poprzedniego posta z drugiego argumentu prevChildKey
.
Zmieniono element podrzędny
Zdarzenie child_changed
jest wywoływane za każdym razem, gdy węzeł podrzędny zostanie zmodyfikowany. Obejmuje to wszelkie modyfikacje potomków węzła podrzędnego. Jest ona zwykle używana w połączeniu z elementami child_added
i child_removed
, aby reagować na zmiany w liście elementów. Zrzut przekazany do wywołania zwrotnego zdarzenia zawiera zaktualizowane dane elementu podrzędnego.
Aby odczytać zaktualizowane dane w postach na blogu po ich edytowaniu, możesz użyć child_changed
:
Java
ref.addChildEventListener(new ChildEventListener() { @Override public void onChildAdded(DataSnapshot dataSnapshot, String prevChildKey) {} @Override public void onChildChanged(DataSnapshot dataSnapshot, String prevChildKey) { Post changedPost = dataSnapshot.getValue(Post.class); System.out.println("The updated post title is: " + changedPost.title); } @Override public void onChildRemoved(DataSnapshot dataSnapshot) {} @Override public void onChildMoved(DataSnapshot dataSnapshot, String prevChildKey) {} @Override public void onCancelled(DatabaseError databaseError) {} });
Node.js
// Get the data on a post that has changed ref.on('child_changed', (snapshot) => { const changedPost = snapshot.val(); console.log('The updated post title is ' + changedPost.title); });
Child Removed
Zdarzenie child_removed
jest wywoływane, gdy usuwasz bezpośrednie dziecko. Zwykle jest używany w połączeniu z atrybutami child_added
i child_changed
. Zrzut podany do wywołania zdarzenia zawiera dane usuniętego podrzędnego.
W przypadku bloga możesz użyć child_removed
, aby wysłać do konsoli powiadomienie o usuniętym poście:
Java
ref.addChildEventListener(new ChildEventListener() { @Override public void onChildAdded(DataSnapshot dataSnapshot, String prevChildKey) {} @Override public void onChildChanged(DataSnapshot dataSnapshot, String prevChildKey) {} @Override public void onChildRemoved(DataSnapshot dataSnapshot) { Post removedPost = dataSnapshot.getValue(Post.class); System.out.println("The blog post titled " + removedPost.title + " has been deleted"); } @Override public void onChildMoved(DataSnapshot dataSnapshot, String prevChildKey) {} @Override public void onCancelled(DatabaseError databaseError) {} });
Node.js
// Get a reference to our posts const ref = db.ref('server/saving-data/fireblog/posts'); // Get the data on a post that has been removed ref.on('child_removed', (snapshot) => { const deletedPost = snapshot.val(); console.log('The blog post titled \'' + deletedPost.title + '\' has been deleted'); });
Child Moved
Zdarzenie child_moved
jest używane podczas pracy z uporządkowanymi danymi. Zostało to opisane w następnej sekcji.
Gwarancje dotyczące wydarzeń
Baza danych Firebase zapewnia kilka ważnych gwarancji dotyczących zdarzeń:
Gwarancje zdarzeń bazy danych |
---|
Zdarzenia będą zawsze wywoływane, gdy zmieni się stan lokalny. |
Zdarzenia zawsze odzwierciedlają prawidłowy stan danych, nawet jeśli operacje lokalne lub czasowanie powodują tymczasowe różnice, np. tymczasową utratę połączenia z siecią. |
Zapisy z pojedynczego klienta są zawsze zapisywane na serwerze i przesyłane do innych użytkowników w kolejności. |
Zdarzenia wartości są zawsze wyzwalane jako ostatnie i gwarantują uwzględnienie aktualizacji ze wszystkich innych zdarzeń, które wystąpiły przed wykonaniem zrzutu. |
Ponieważ zdarzenia wartości są zawsze wywoływane jako ostatnie, ten przykład będzie zawsze działał:
Java
final AtomicInteger count = new AtomicInteger(); ref.addChildEventListener(new ChildEventListener() { @Override public void onChildAdded(DataSnapshot dataSnapshot, String prevChildKey) { // New child added, increment count int newCount = count.incrementAndGet(); System.out.println("Added " + dataSnapshot.getKey() + ", count is " + newCount); } // ... }); // The number of children will always be equal to 'count' since the value of // the dataSnapshot here will include every child_added event triggered before this point. ref.addListenerForSingleValueEvent(new ValueEventListener() { @Override public void onDataChange(DataSnapshot dataSnapshot) { long numChildren = dataSnapshot.getChildrenCount(); System.out.println(count.get() + " == " + numChildren); } @Override public void onCancelled(DatabaseError databaseError) {} });
Node.js
let count = 0; ref.on('child_added', (snap) => { count++; console.log('added:', snap.key); }); // length will always equal count, since snap.val() will include every child_added event // triggered before this point ref.once('value', (snap) => { console.log('initial data loaded!', snap.numChildren() === count); });
Odłączanie wywołań zwrotnych
Wywołania zwrotne są usuwane przez określenie typu zdarzenia i funkcji wywołania zwrotnego do usunięcia:
Java
// Create and attach listener ValueEventListener listener = new ValueEventListener() { // ... }; ref.addValueEventListener(listener); // Remove listener ref.removeEventListener(listener);
Node.js
ref.off('value', originalCallback);
Jeśli przekazujesz parametr on()
w kontekście zakresu, musisz go przekazać podczas odłączania wywołania zwrotnego:
Java
// Not applicable for Java
Node.js
ref.off('value', originalCallback, ctx);
Jeśli chcesz usunąć wszystkie wywołania zwrotne w danej lokalizacji, wykonaj te czynności:
Java
// No Java equivalent, listeners must be removed individually.
Node.js
// Remove all value callbacks ref.off('value'); // Remove all callbacks of any type ref.off();
Jednorazowy odczyt danych
W niektórych przypadkach może być przydatne, aby połączenie zwrotne zostało wykonane raz, a następnie natychmiast usunięte. Aby ułatwić Ci to zadanie, opracowaliśmy funkcję pomocniczą:
Java
ref.addListenerForSingleValueEvent(new ValueEventListener() { @Override public void onDataChange(DataSnapshot dataSnapshot) { // ... } @Override public void onCancelled(DatabaseError databaseError) { // ... } });
Node.js
ref.once('value', (data) => { // do some stuff once });
Python
# Import database module. from firebase_admin import db # Get a database reference to our posts ref = db.reference('server/saving-data/fireblog/posts') # Read the data at the posts reference (this is a blocking operation) print(ref.get())
Go
// Create a database client from App. client, err := app.Database(ctx) if err != nil { log.Fatalln("Error initializing database client:", err) } // Get a database reference to our posts ref := client.NewRef("server/saving-data/fireblog/posts") // Read the data at the posts reference (this is a blocking operation) var post Post if err := ref.Get(ctx, &post); err != nil { log.Fatalln("Error reading value:", err) }
Wysyłanie zapytań o dane
Zapytania do bazy danych Firebase pozwalają selektywnie pobierać dane na podstawie różnych czynników. Aby utworzyć zapytanie w bazie danych, musisz najpierw określić sposób sortowania danych za pomocą jednej z funkcji sortowania: orderByChild()
, orderByKey()
lub orderByValue()
. Następnie możesz je łączyć z 5 innymi metodami, aby przeprowadzać złożone zapytania: limitToFirst()
, limitToLast()
, startAt()
, endAt()
i equalTo()
.
Ponieważ wszyscy w Firebase uważamy dinozaury za fajne, użyjemy fragmentu z przykładowej bazy danych zawierającej informacje o dinosaurach, aby pokazać, jak możesz wysyłać zapytania o dane do bazy danych Firebase:
{ "lambeosaurus": { "height" : 2.1, "length" : 12.5, "weight": 5000 }, "stegosaurus": { "height" : 4, "length" : 9, "weight" : 2500 } }
Dane można sortować na 3 sposoby: według klucza podrzędnego, klucza lub wartości. Podstawowe zapytanie do bazy danych zaczyna się od jednej z tych funkcji sortowania, które są opisane poniżej.
sortowanie według określonego klucza podrzędnego;
Możesz sortować węzły według wspólnego klucza podrzędnego, przekazując ten klucz do funkcji orderByChild()
. Aby na przykład odczytać wszystkie dinozaury posortowane według wysokości, wykonaj te czynności:
Java
public static class Dinosaur { public int height; public int weight; public Dinosaur(int height, int weight) { // ... } } final DatabaseReference dinosaursRef = database.getReference("dinosaurs"); dinosaursRef.orderByChild("height").addChildEventListener(new ChildEventListener() { @Override public void onChildAdded(DataSnapshot dataSnapshot, String prevChildKey) { Dinosaur dinosaur = dataSnapshot.getValue(Dinosaur.class); System.out.println(dataSnapshot.getKey() + " was " + dinosaur.height + " meters tall."); } // ... });
Node.js
const ref = db.ref('dinosaurs'); ref.orderByChild('height').on('child_added', (snapshot) => { console.log(snapshot.key + ' was ' + snapshot.val().height + ' meters tall'); });
Python
ref = db.reference('dinosaurs') snapshot = ref.order_by_child('height').get() for key, val in snapshot.items(): print('{0} was {1} meters tall'.format(key, val))
Go
// Dinosaur is a json-serializable type. type Dinosaur struct { Height int `json:"height"` Width int `json:"width"` } ref := client.NewRef("dinosaurs") results, err := ref.OrderByChild("height").GetOrdered(ctx) if err != nil { log.Fatalln("Error querying database:", err) } for _, r := range results { var d Dinosaur if err := r.Unmarshal(&d); err != nil { log.Fatalln("Error unmarshaling result:", err) } fmt.Printf("%s was %d meteres tall", r.Key(), d.Height) }
Każdy węzeł, który nie ma klucza podrzędnego, na podstawie którego wysyłamy zapytanie, jest sortowany z wartością null
, co oznacza, że będzie pierwszy w kolejności. Szczegółowe informacje o tym, jak są sortowane dane, znajdziesz w sekcji Sortowanie danych.
Zapytania można też sortować według elementów podrzędnych ukrytych głęboko w hierarchii, a nie tylko elementów podrzędnych znajdujących się na 1 poziomie. Jest to przydatne, jeśli masz głęboko zagnieżdżone dane, np. takie:
{ "lambeosaurus": { "dimensions": { "height" : 2.1, "length" : 12.5, "weight": 5000 } }, "stegosaurus": { "dimensions": { "height" : 4, "length" : 9, "weight" : 2500 } } }
Aby teraz zapytać o wysokość, możesz użyć pełnej ścieżki do obiektu zamiast pojedynczego klucza:
Java
dinosaursRef.orderByChild("dimensions/height").addChildEventListener(new ChildEventListener() { @Override public void onChildAdded(DataSnapshot dataSnapshot, String prevChildKey) { // ... } // ... });
Node.js
const ref = db.ref('dinosaurs'); ref.orderByChild('dimensions/height').on('child_added', (snapshot) => { console.log(snapshot.key + ' was ' + snapshot.val().height + ' meters tall'); });
Python
ref = db.reference('dinosaurs') snapshot = ref.order_by_child('dimensions/height').get() for key, val in snapshot.items(): print('{0} was {1} meters tall'.format(key, val))
Go
ref := client.NewRef("dinosaurs") results, err := ref.OrderByChild("dimensions/height").GetOrdered(ctx) if err != nil { log.Fatalln("Error querying database:", err) } for _, r := range results { var d Dinosaur if err := r.Unmarshal(&d); err != nil { log.Fatalln("Error unmarshaling result:", err) } fmt.Printf("%s was %d meteres tall", r.Key(), d.Height) }
Zapytania mogą być sortowane według tylko jednego klucza naraz. Wywołanie funkcji orderByChild()
kilka razy w przypadku tego samego zapytania powoduje błąd.
Sortowanie według klucza
Możesz też sortować węzły według kluczy za pomocą metody orderByKey()
. W tym przykładzie wszystkie dinozaury są odczytywane w kolejności alfabetycznej:
Java
dinosaursRef.orderByKey().addChildEventListener(new ChildEventListener() { @Override public void onChildAdded(DataSnapshot dataSnapshot, String prevChildKey) { System.out.println(dataSnapshot.getKey()); } // ... });
Node.js
var ref = db.ref('dinosaurs'); ref.orderByKey().on('child_added', (snapshot) => { console.log(snapshot.key); });
Python
ref = db.reference('dinosaurs') snapshot = ref.order_by_key().get() print(snapshot)
Go
ref := client.NewRef("dinosaurs") results, err := ref.OrderByKey().GetOrdered(ctx) if err != nil { log.Fatalln("Error querying database:", err) } snapshot := make([]Dinosaur, len(results)) for i, r := range results { var d Dinosaur if err := r.Unmarshal(&d); err != nil { log.Fatalln("Error unmarshaling result:", err) } snapshot[i] = d } fmt.Println(snapshot)
Sortowanie według wartości
Korzystając z metody orderByValue()
, możesz sortować węzły według wartości ich kluczy podrzędnych. Załóżmy, że dinozaury biorą udział w zawodach sportowych, a Ty śledzisz ich wyniki w takim formacie:
{ "scores": { "bruhathkayosaurus" : 55, "lambeosaurus" : 21, "linhenykus" : 80, "pterodactyl" : 93, "stegosaurus" : 5, "triceratops" : 22 } }
Aby posortować dinozaury według ich wyniku, możesz utworzyć takie zapytanie:
Java
DatabaseReference scoresRef = database.getReference("scores"); scoresRef.orderByValue().addChildEventListener(new ChildEventListener() { @Override public void onChildAdded(DataSnapshot dataSnapshot, String prevChildKey) { System.out.println("The " + dataSnapshot.getKey() + " score is " + dataSnapshot.getValue()); } // ... });
Node.js
const scoresRef = db.ref('scores'); scoresRef.orderByValue().on('value', (snapshot) => { snapshot.forEach((data) => { console.log('The ' + data.key + ' dinosaur\'s score is ' + data.val()); }); });
Python
ref = db.reference('scores') snapshot = ref.order_by_value().get() for key, val in snapshot.items(): print('The {0} dinosaur\'s score is {1}'.format(key, val))
Go
ref := client.NewRef("scores") results, err := ref.OrderByValue().GetOrdered(ctx) if err != nil { log.Fatalln("Error querying database:", err) } for _, r := range results { var score int if err := r.Unmarshal(&score); err != nil { log.Fatalln("Error unmarshaling result:", err) } fmt.Printf("The %s dinosaur's score is %d\n", r.Key(), score) }
Więcej informacji o sortowaniu wartości null
, wartości logicznych, ciągów tekstowych i wartości obiektów podczas używania funkcji orderByValue()
znajdziesz w sekcji Uporządkowanie danych.
Zapytania złożone
Teraz, gdy już wiesz, jak są uporządkowane Twoje dane, możesz tworzyć bardziej złożone zapytania, korzystając z opisanych niżej metod limit i range.
Ogranicz zapytania
Zapytania limitToFirst()
i limitToLast()
służą do ustawiania maksymalnej liczby elementów podrzędnych, które mają być synchronizowane w przypadku danego wywołania zwrotnego. Jeśli ustawisz limit na 100, początkowo otrzymasz tylko maksymalnie 100 zdarzeń child_added
. Jeśli w bazie danych jest mniej niż 100 wiadomości, dla każdej wiadomości będzie wywoływane zdarzenie child_added
. Jeśli jednak masz ich ponad 100, otrzymasz zdarzenie child_added
tylko dla 100 z nich. Jeśli używasz funkcji limitToFirst()
, są to pierwsze 100 uporządkowanych wiadomości, a jeśli limitToLast()
– ostatnie 100 uporządkowanych wiadomości. Gdy elementy się zmieniają, otrzymujesz zdarzenia child_added
dotyczące elementów, które wchodzą do zapytania, oraz zdarzenia child_removed
dotyczące elementów, które je opuszczają. Dzięki temu łączna liczba pozostaje równa 100.
Korzystając z bazy danych o dinosaurach i orderByChild()
, możesz znaleźć 2 najcięższe dinozaury:
Java
dinosaursRef.orderByChild("weight").limitToLast(2).addChildEventListener(new ChildEventListener() { @Override public void onChildAdded(DataSnapshot dataSnapshot, String prevChildKey) { System.out.println(dataSnapshot.getKey()); } // ... });
Node.js
const ref = db.ref('dinosaurs'); ref.orderByChild('weight').limitToLast(2).on('child_added', (snapshot) => { console.log(snapshot.key); });
Python
ref = db.reference('dinosaurs') snapshot = ref.order_by_child('weight').limit_to_last(2).get() for key in snapshot: print(key)
Go
ref := client.NewRef("dinosaurs") results, err := ref.OrderByChild("weight").LimitToLast(2).GetOrdered(ctx) if err != nil { log.Fatalln("Error querying database:", err) } for _, r := range results { fmt.Println(r.Key()) }
Funkcja child_added
jest wywoływana dokładnie 2 razy, chyba że w bazie danych jest mniej niż 2 dinozaury. Zostanie też wywoływana za każdym razem, gdy do bazy danych zostanie dodany nowy, cięższy dinozaur.
W Pythonie zapytanie zwraca bezpośrednio OrderedDict
zawierające 2 najcięższe dinozaury.
Podobnie możesz znaleźć 2 najkrótsze dinozaury, używając zapytania limitToFirst()
:
Java
dinosaursRef.orderByChild("weight").limitToFirst(2).addChildEventListener(new ChildEventListener() { @Override public void onChildAdded(DataSnapshot dataSnapshot, String prevChildKey) { System.out.println(dataSnapshot.getKey()); } // ... });
Node.js
const ref = db.ref('dinosaurs'); ref.orderByChild('height').limitToFirst(2).on('child_added', (snapshot) => { console.log(snapshot.key); });
Python
ref = db.reference('dinosaurs') snapshot = ref.order_by_child('height').limit_to_first(2).get() for key in snapshot: print(key)
Go
ref := client.NewRef("dinosaurs") results, err := ref.OrderByChild("height").LimitToFirst(2).GetOrdered(ctx) if err != nil { log.Fatalln("Error querying database:", err) } for _, r := range results { fmt.Println(r.Key()) }
Funkcja child_added
jest wywoływana dokładnie 2 razy, chyba że w bazie danych jest mniej niż 2 dinozaury. Zostanie ona też wywołana ponownie, jeśli jeden z pierwszych dwóch dinozaurów zostanie usunięty z bazy danych, ponieważ nowy dinozaur będzie drugim najkrótszym. W Pythonie zapytanie bezpośrednio zwraca wartość OrderedDict
zawierającą najkrótsze dinozaury.
Możesz też wykonywać zapytania o limity za pomocą funkcji orderByValue()
. Jeśli chcesz utworzyć tabelę wyników z 3 dinosaurami sportowymi dinozaurów o najwyższej liczbie punktów, wykonaj te czynności:
Java
scoresRef.orderByValue().limitToFirst(3).addChildEventListener(new ChildEventListener() { @Override public void onChildAdded(DataSnapshot dataSnapshot, String prevChildKey) { System.out.println("The " + dataSnapshot.getKey() + " score is " + dataSnapshot.getValue()); } // ... });
Node.js
const scoresRef = db.ref('scores'); scoresRef.orderByValue().limitToLast(3).on('value', (snapshot) =>{ snapshot.forEach((data) => { console.log('The ' + data.key + ' dinosaur\'s score is ' + data.val()); }); });
Python
scores_ref = db.reference('scores') snapshot = scores_ref.order_by_value().limit_to_last(3).get() for key, val in snapshot.items(): print('The {0} dinosaur\'s score is {1}'.format(key, val))
Go
ref := client.NewRef("scores") results, err := ref.OrderByValue().LimitToLast(3).GetOrdered(ctx) if err != nil { log.Fatalln("Error querying database:", err) } for _, r := range results { var score int if err := r.Unmarshal(&score); err != nil { log.Fatalln("Error unmarshaling result:", err) } fmt.Printf("The %s dinosaur's score is %d\n", r.Key(), score) }
Zapytania dotyczące zakresu
Korzystając z parametrów startAt()
, endAt()
i equalTo()
, możesz wybierać dowolne punkty początkowe i końcowe zapytań. Jeśli na przykład chcesz znaleźć wszystkie dinozaury mające co najmniej 3 metry wysokości, możesz połączyć elementy orderByChild()
i startAt()
:
Java
dinosaursRef.orderByChild("height").startAt(3).addChildEventListener(new ChildEventListener() { @Override public void onChildAdded(DataSnapshot dataSnapshot, String prevChildKey) { System.out.println(dataSnapshot.getKey()); } // ... });
Node.js
const ref = db.ref('dinosaurs'); ref.orderByChild('height').startAt(3).on('child_added', (snapshot) => { console.log(snapshot.key); });
Python
ref = db.reference('dinosaurs') snapshot = ref.order_by_child('height').start_at(3).get() for key in snapshot: print(key)
Go
ref := client.NewRef("dinosaurs") results, err := ref.OrderByChild("height").StartAt(3).GetOrdered(ctx) if err != nil { log.Fatalln("Error querying database:", err) } for _, r := range results { fmt.Println(r.Key()) }
Za pomocą endAt()
możesz znaleźć wszystkie dinozaury, których nazwy występują w alfabecie przed nazwą Pterodactyl:
Java
dinosaursRef.orderByKey().endAt("pterodactyl").addChildEventListener(new ChildEventListener() { @Override public void onChildAdded(DataSnapshot dataSnapshot, String prevChildKey) { System.out.println(dataSnapshot.getKey()); } // ... });
Node.js
const ref = db.ref('dinosaurs'); ref.orderByKey().endAt('pterodactyl').on('child_added', (snapshot) => { console.log(snapshot.key); });
Python
ref = db.reference('dinosaurs') snapshot = ref.order_by_key().end_at('pterodactyl').get() for key in snapshot: print(key)
Go
ref := client.NewRef("dinosaurs") results, err := ref.OrderByKey().EndAt("pterodactyl").GetOrdered(ctx) if err != nil { log.Fatalln("Error querying database:", err) } for _, r := range results { fmt.Println(r.Key()) }
Możesz połączyć operatory startAt()
i endAt()
, aby ograniczyć oba końce zapytania. W tym przykładzie zostaną znalezione wszystkie dinozaury, których nazwa zaczyna się od litery „b”:
Java
dinosaursRef.orderByKey().startAt("b").endAt("b\uf8ff").addChildEventListener(new ChildEventListener() { @Override public void onChildAdded(DataSnapshot dataSnapshot, String prevChildKey) { System.out.println(dataSnapshot.getKey()); } // ... });
Node.js
var ref = db.ref('dinosaurs'); ref.orderByKey().startAt('b').endAt('b\uf8ff').on('child_added', (snapshot) => { console.log(snapshot.key); });
Python
ref = db.reference('dinosaurs') snapshot = ref.order_by_key().start_at('b').end_at(u'b\uf8ff').get() for key in snapshot: print(key)
Go
ref := client.NewRef("dinosaurs") results, err := ref.OrderByKey().StartAt("b").EndAt("b\uf8ff").GetOrdered(ctx) if err != nil { log.Fatalln("Error querying database:", err) } for _, r := range results { fmt.Println(r.Key()) }
Metoda equalTo()
umożliwia filtrowanie na podstawie dopasowań ścisłych. Podobnie jak w przypadku innych zapytań dotyczących zakresu, zostanie ono wykonane w przypadku każdego pasującego węzła podrzędnego. Za pomocą tego zapytania możesz na przykład znaleźć wszystkie dinozaury o wysokości 25 metrów:
Java
dinosaursRef.orderByChild("height").equalTo(25).addChildEventListener(new ChildEventListener() { @Override public void onChildAdded(DataSnapshot dataSnapshot, String prevChildKey) { System.out.println(dataSnapshot.getKey()); } // ... });
Node.js
const ref = db.ref('dinosaurs'); ref.orderByChild('height').equalTo(25).on('child_added', (snapshot) => { console.log(snapshot.key); });
Python
ref = db.reference('dinosaurs') snapshot = ref.order_by_child('height').equal_to(25).get() for key in snapshot: print(key)
Go
ref := client.NewRef("dinosaurs") results, err := ref.OrderByChild("height").EqualTo(25).GetOrdered(ctx) if err != nil { log.Fatalln("Error querying database:", err) } for _, r := range results { fmt.Println(r.Key()) }
Zapytania zakresowe są też przydatne, gdy chcesz podzielić dane na strony.
Łączę wszystko w całość
Możesz łączyć te techniki, aby tworzyć złożone zapytania. Możesz na przykład znaleźć nazwę dinozaura, który jest tylko krótszy od stegozaura:
Java
dinosaursRef.child("stegosaurus").child("height").addValueEventListener(new ValueEventListener() { @Override public void onDataChange(DataSnapshot stegoHeightSnapshot) { Integer favoriteDinoHeight = stegoHeightSnapshot.getValue(Integer.class); Query query = dinosaursRef.orderByChild("height").endAt(favoriteDinoHeight).limitToLast(2); query.addValueEventListener(new ValueEventListener() { @Override public void onDataChange(DataSnapshot dataSnapshot) { // Data is ordered by increasing height, so we want the first entry DataSnapshot firstChild = dataSnapshot.getChildren().iterator().next(); System.out.println("The dinosaur just shorter than the stegosaurus is: " + firstChild.getKey()); } @Override public void onCancelled(DatabaseError databaseError) { // ... } }); } @Override public void onCancelled(DatabaseError databaseError) { // ... } });
Node.js
const ref = db.ref('dinosaurs'); ref.child('stegosaurus').child('height').on('value', (stegosaurusHeightSnapshot) => { const favoriteDinoHeight = stegosaurusHeightSnapshot.val(); const queryRef = ref.orderByChild('height').endAt(favoriteDinoHeight).limitToLast(2); queryRef.on('value', (querySnapshot) => { if (querySnapshot.numChildren() === 2) { // Data is ordered by increasing height, so we want the first entry querySnapshot.forEach((dinoSnapshot) => { console.log('The dinosaur just shorter than the stegasaurus is ' + dinoSnapshot.key); // Returning true means that we will only loop through the forEach() one time return true; }); } else { console.log('The stegosaurus is the shortest dino'); } }); });
Python
ref = db.reference('dinosaurs') favotire_dino_height = ref.child('stegosaurus').child('height').get() query = ref.order_by_child('height').end_at(favotire_dino_height).limit_to_last(2) snapshot = query.get() if len(snapshot) == 2: # Data is ordered by increasing height, so we want the first entry. # Second entry is stegosarus. for key in snapshot: print('The dinosaur just shorter than the stegosaurus is {0}'.format(key)) return else: print('The stegosaurus is the shortest dino')
Go
ref := client.NewRef("dinosaurs") var favDinoHeight int if err := ref.Child("stegosaurus").Child("height").Get(ctx, &favDinoHeight); err != nil { log.Fatalln("Error querying database:", err) } query := ref.OrderByChild("height").EndAt(favDinoHeight).LimitToLast(2) results, err := query.GetOrdered(ctx) if err != nil { log.Fatalln("Error querying database:", err) } if len(results) == 2 { // Data is ordered by increasing height, so we want the first entry. // Second entry is stegosarus. fmt.Printf("The dinosaur just shorter than the stegosaurus is %s\n", results[0].Key()) } else { fmt.Println("The stegosaurus is the shortest dino") }
W jaki sposób dane są porządkowane
W tej sekcji wyjaśniamy, jak są sortowane dane przy użyciu każdej z 4 funkcji sortowania.
Kolejność według elementu podrzędnego
Gdy używasz orderByChild()
, dane zawierające określony klucz podrzędny są uporządkowane w taki sposób:
- Najpierw są wybierane elementy podrzędne, dla których wartość atrybutu podrzędnego to
null
. - Następnie podawane są elementy podrzędne o wartości
false
dla określonego klucza podrzędnego. Jeśli wiele elementów podrzędnych ma wartośćfalse
, są one sortowane alfabetycznie według klucza. - Następnie podawane są elementy podrzędne o wartości
true
dla określonego klucza podrzędnego. Jeśli wiele elementów podrzędnych ma wartośćtrue
, są one sortowane alfabetycznie według klucza. - Następne są elementy podrzędne z wartością liczbową, posortowane w kolejności rosnącej. Jeśli wiele elementów podrzędnych ma tę samą wartość numeryczną w wybranym węźle podrzędnym, są one sortowane według klucza.
- 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 znajdują się na końcu i są posortowane leksykograficznie według klucza w kolejności rosnącej.
orderByKey
Gdy używasz parametru orderByKey()
do sortowania danych, są one zwracane w kolejności rosnącej według klucza w taki sposób: Pamiętaj, że klucze mogą być tylko ciągami znaków.
- Najpierw wyświetlane są dzieci, których klucz można przeanalizować jako 32-bitową liczbę całkowitą, w kolejności rosnącej.
- Kolejne są elementy podrzędne z wartością ciągu znaków jako kluczem, posortowane leksykograficznie w kolejności rosnącej.
orderByValue
W przypadku użycia orderByValue()
podrzędne elementy są sortowane według wartości. Kryteria sortowania są takie same jak w orderByChild()
, z tym że zamiast wartości określonego klucza podrzędnego używana jest wartość węzła.