Pobieram dane

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.

  1. 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.
  2. 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:

  1. Elementy podrzędne z wartością null w określonym kluczu podrzędnym są wyświetlane jako pierwsze.
  2. 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.
  3. 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.
  4. 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.
  5. Ciągi znaków znajdują się po liczbach i są sortowane leksykograficznie w kolejności rosnącej. Jeśli wiele elementów podrzędnych ma tę samą wartość w określonym węźle podrzędnym, są one uporządkowane leksykograficznie według klucza.
  6. Obiekty są na końcu i 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.

  1. Elementy podrzędne z kluczem, który można przeanalizować jako 32-bitową liczbę całkowitą, są posortowane w kolejności rosnącej.
  2. 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.