Pobieranie danych

W tym dokumencie opisano podstawy pobierania danych z bazy danych, sposobu porządkowania danych i wykonywania prostych zapytań na danych. Pobieranie danych w pakiecie Admin SDK jest implementowane nieco inaczej w różnych językach programowania.

  1. Słuchacze asynchroniczne: dane przechowywane w bazie danych Firebase Realtime Database są pobierane poprzez dołączenie odbiornika asynchronicznego do odniesienia do bazy danych. Odbiornik jest uruchamiany raz dla stanu początkowego danych i ponownie za każdym razem, gdy dane się zmieniają. Nasłuchiwanie zdarzeń może odbierać kilka różnych typów zdarzeń . Ten tryb pobierania danych jest obsługiwany w pakietach SDK Java, Node.js i Python Admin.
  2. Blokowanie odczytów: dane przechowywane w bazie danych Firebase Realtime Database są pobierane poprzez wywołanie metody blokowania w odniesieniu do bazy danych, która zwraca dane przechowywane w odwołaniu. Każde wywołanie metody jest operacją jednorazową. Oznacza to, że zestaw SDK nie rejestruje żadnych wywołań zwrotnych nasłuchujących kolejnych aktualizacji danych. Ten model pobierania danych jest obsługiwany w pakietach SDK Python i Go Admin.

Pierwsze kroki

Powróćmy do przykładu blogowania z poprzedniego artykułu, aby zrozumieć, jak czytać dane z bazy danych Firebase. Przypomnij sobie, że wpisy 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 swoich postów, możesz wykonać następujące czynności:

Jawa
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);
}); 
Pyton
# 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())
Iść

// 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 Node.js i Java funkcja nasłuchiwania jest wywoływana za każdym razem, gdy do bazy danych dodawane są nowe dane i nie trzeba w tym celu pisać żadnego dodatkowego kodu.

W Javie i Node.js funkcja wywołania zwrotnego odbiera DataSnapshot , który jest migawką danych. Migawka to obraz danych w określonym odwołaniu do bazy danych w jednym momencie. Wywołanie val() / getValue() na migawce zwraca obiektową reprezentację danych specyficzną dla języka. Jeśli w lokalizacji odniesienia nie istnieją żadne dane, wartość migawki wynosi null . Metoda get() w Pythonie bezpośrednio zwraca reprezentację danych w języku Python. Funkcja Get() w Go rozdziela 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ść odwołania do bazy danych Firebase, nawet jeśli zmienił się tylko jeden fragment danych. value to jeden z pięciu różnych typów zdarzeń wymienionych poniżej, których można użyć do odczytu danych z bazy danych.

Przeczytaj typy zdarzeń w Javie i Node.js

Wartość

Zdarzenie value służy do odczytania statycznego migawki zawartości w danej ścieżce bazy danych, takiej jaka istniała w momencie zdarzenia read. Jest wyzwalany raz przy danych początkowych i ponownie przy każdej zmianie danych. Do wywołania zwrotnego zdarzenia przekazywana jest migawka zawierająca wszystkie dane w tej lokalizacji, w tym dane podrzędne. W powyższym przykładzie kodu value zwróciła wszystkie posty na blogu w Twojej aplikacji. Za każdym razem, gdy dodawany jest nowy wpis na blogu, funkcja wywołania zwrotnego zwróci wszystkie posty.

Dodano dziecko

Zdarzenie child_added jest zwykle używane podczas pobierania listy elementów z bazy danych. W przeciwieństwie do value , która zwraca całą zawartość lokalizacji, child_added jest wyzwalana raz dla każdego istniejącego elementu podrzędnego, a następnie ponownie za każdym razem, gdy do określonej ścieżki dodawane jest nowe dziecko. Do wywołania zwrotnego zdarzenia przekazywana jest migawka zawierająca dane nowego dziecka. Dla celów porządkowych przekazywany jest także drugi argument zawierający klucz poprzedniego dziecka.

Jeśli chcesz pobrać tylko dane dotyczące każdego nowego wpisu dodanego do aplikacji do blogowania, możesz użyć child_added :

Jawa
ref.addChildEventListener(new ChildEventListener() {
  @Override
  public void onChildAdded(DataSnapshot dataSnapshot, String prevChildKey) {
    Post newPost = dataSnapshot.getValue(Post.class);
    System.out.println("Author: " + newPost.author);
    System.out.println("Title: " + newPost.title);
    System.out.println("Previous Post ID: " + prevChildKey);
  }

  @Override
  public void onChildChanged(DataSnapshot dataSnapshot, String prevChildKey) {}

  @Override
  public void onChildRemoved(DataSnapshot dataSnapshot) {}

  @Override
  public void onChildMoved(DataSnapshot dataSnapshot, String prevChildKey) {}

  @Override
  public void onCancelled(DatabaseError databaseError) {}
});
Node.js
// Retrieve new posts as they are added to our database
ref.on('child_added', (snapshot, prevChildKey) => {
  const newPost = snapshot.val();
  console.log('Author: ' + newPost.author);
  console.log('Title: ' + newPost.title);
  console.log('Previous Post ID: ' + prevChildKey);
});

W tym przykładzie migawka będzie zawierać obiekt z indywidualnym wpisem na blogu. Ponieważ pakiet SDK konwertuje posty na obiekty, pobierając wartość, masz dostęp do właściwości autora i tytułu wpisu, wywołując odpowiednio author i title . Masz także dostęp do identyfikatora poprzedniego wpisu z drugiego argumentu prevChildKey .

Dziecko zmienione

Zdarzenie child_changed jest wyzwalane za każdym razem, gdy modyfikuje się węzeł podrzędny. Obejmuje to wszelkie modyfikacje elementów podrzędnych węzła podrzędnego. Zwykle jest używany w połączeniu z child_added i child_removed , aby odpowiedzieć na zmiany na liście elementów. Migawka przekazana do wywołania zwrotnego zdarzenia zawiera zaktualizowane dane elementu podrzędnego.

Możesz użyć child_changed aby przeczytać zaktualizowane dane na temat postów na blogu, gdy są one edytowane:

Jawa
ref.addChildEventListener(new ChildEventListener() {
  @Override
  public void onChildAdded(DataSnapshot dataSnapshot, String prevChildKey) {}

  @Override
  public void onChildChanged(DataSnapshot dataSnapshot, String prevChildKey) {
    Post changedPost = dataSnapshot.getValue(Post.class);
    System.out.println("The updated post title is: " + changedPost.title);
  }

  @Override
  public void onChildRemoved(DataSnapshot dataSnapshot) {}

  @Override
  public void onChildMoved(DataSnapshot dataSnapshot, String prevChildKey) {}

  @Override
  public void onCancelled(DatabaseError databaseError) {}
});
Node.js
// Get the data on a post that has changed
ref.on('child_changed', (snapshot) => {
  const changedPost = snapshot.val();
  console.log('The updated post title is ' + changedPost.title);
});

Dziecko usunięte

Zdarzenie child_removed jest wyzwalane w przypadku usunięcia bezpośredniego dziecka. Zwykle jest używany w połączeniu z child_added i child_changed . Migawka przekazana do wywołania zwrotnego zdarzenia zawiera dane usuniętego elementu podrzędnego.

W przykładzie bloga możesz użyć child_removed , aby zarejestrować powiadomienie o usuniętym poście na konsoli:

Jawa
ref.addChildEventListener(new ChildEventListener() {
  @Override
  public void onChildAdded(DataSnapshot dataSnapshot, String prevChildKey) {}

  @Override
  public void onChildChanged(DataSnapshot dataSnapshot, String prevChildKey) {}

  @Override
  public void onChildRemoved(DataSnapshot dataSnapshot) {
    Post removedPost = dataSnapshot.getValue(Post.class);
    System.out.println("The blog post titled " + removedPost.title + " has been deleted");
  }

  @Override
  public void onChildMoved(DataSnapshot dataSnapshot, String prevChildKey) {}

  @Override
  public void onCancelled(DatabaseError databaseError) {}
});
Node.js
// Get a reference to our posts
const ref = db.ref('server/saving-data/fireblog/posts');

// Get the data on a post that has been removed
ref.on('child_removed', (snapshot) => {
  const deletedPost = snapshot.val();
  console.log('The blog post titled \'' + deletedPost.title + '\' has been deleted');
});

Dziecko przeniesione

Zdarzenie child_moved jest używane podczas pracy z uporządkowanymi danymi, co zostało omówione w następnej sekcji .

Gwarancje wydarzeń

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

Gwarancje zdarzeń w bazie danych
Zdarzenia będą zawsze wyzwalane w przypadku zmiany stanu lokalnego.
Zdarzenia zawsze ostatecznie odzwierciedlają prawidłowy stan danych, nawet w przypadkach, gdy lokalne operacje lub synchronizacja powodują tymczasowe różnice, na przykład chwilową utratę połączenia sieciowego.
Zapisy od jednego klienta będą zawsze zapisywane na serwerze i rozsyłane do innych użytkowników w odpowiedniej kolejności.
Zdarzenia wartości są zawsze wyzwalane jako ostatnie i gwarantują, że będą zawierać aktualizacje wszelkich innych zdarzeń, które miały miejsce przed wykonaniem danej migawki.

Ponieważ zdarzenia wartości są zawsze wyzwalane jako ostatnie, poniższy przykład zawsze będzie działać:

Jawa
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 usuwa się poprzez określenie typu zdarzenia i funkcji wywołania zwrotnego, która ma zostać usunięta, jak poniżej:

Jawa
// Create and attach listener
ValueEventListener listener = new ValueEventListener() {
    // ...
};
ref.addValueEventListener(listener);

// Remove listener
ref.removeEventListener(listener);
Node.js
ref.off('value', originalCallback);

Jeśli przekazałeś kontekst zakresu do on() , należy go przekazać podczas odłączania wywołania zwrotnego:

Jawa
// 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ć następujące czynności:

Jawa
// 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();

Odczyt danych raz

W niektórych przypadkach przydatne może być jednorazowe wywołanie wywołania zwrotnego, a następnie natychmiastowe jego usunięcie. Aby to ułatwić, stworzyliśmy funkcję pomocniczą:

Jawa
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
});
Pyton
# 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())
Iść
// 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)
}

Zapytanie o dane

Dzięki zapytaniom do bazy danych Firebase możesz selektywnie pobierać dane na podstawie różnych czynników. Aby skonstruować zapytanie w bazie danych, zaczynasz od określenia sposobu uporządkowania danych za pomocą jednej z funkcji porządkujących: orderByChild() , orderByKey() lub orderByValue() . Następnie możesz połączyć je z pięcioma innymi metodami, aby przeprowadzać złożone zapytania: limitToFirst() , limitToLast() , startAt() , endAt() i equalTo() .

Ponieważ wszyscy w Firebase uważamy, że dinozaury są całkiem fajne, użyjemy fragmentu przykładowej bazy danych zawierającej fakty o dinozaurach, aby zademonstrować, w jaki sposób można wysyłać zapytania do danych w bazie danych Firebase.:

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

Możesz uporządkować dane na trzy sposoby: według klucza podrzędnego , według klucza lub według wartości . Podstawowe zapytanie do bazy danych rozpoczyna się od jednej z tych funkcji porządkujących, z których każda została wyjaśniona poniżej.

Zamawianie według określonego klucza podrzędnego

Możesz porządkować węzły według wspólnego klucza podrzędnego, przekazując ten klucz do orderByChild() . Na przykład, aby przeczytać wszystkie dinozaury uporządkowane według wzrostu, możesz wykonać następujące czynności:

Jawa
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');
});
Pyton
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))
Iść

// Dinosaur is a json-serializable type.
type Dinosaur struct {
	Height int `json:"height"`
	Width  int `json:"width"`
}

ref := client.NewRef("dinosaurs")

results, err := ref.OrderByChild("height").GetOrdered(ctx)
if err != nil {
	log.Fatalln("Error querying database:", err)
}
for _, r := range results {
	var d Dinosaur
	if err := r.Unmarshal(&d); err != nil {
		log.Fatalln("Error unmarshaling result:", err)
	}
	fmt.Printf("%s was %d meteres tall", r.Key(), d.Height)
}

Każdy węzeł, który nie ma klucza podrzędnego, o który pytamy, jest sortowany z wartością null , co oznacza, że ​​zajmie pierwsze miejsce w kolejności. Aby uzyskać szczegółowe informacje na temat uporządkowania danych, zobacz sekcję Jak uporządkowane są dane .

Zapytania mogą być również porządkowane przez głęboko zagnieżdżone elementy podrzędne, a nie tylko podrzędne o jeden poziom niżej. Jest to przydatne, jeśli masz głęboko zagnieżdżone dane, takie jak to:

{
  "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:

Jawa
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');
});
Pyton
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))
Iść
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 można porządkować tylko według jednego klucza na raz. Wielokrotne wywołanie orderByChild() w tym samym zapytaniu powoduje błąd.

Zamawianie według klucza

Możesz także uporządkować węzły według ich kluczy, korzystając z metody orderByKey() . Poniższy przykład odczytuje wszystkie dinozaury w kolejności alfabetycznej:

Jawa
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);
});
Pyton
ref = db.reference('dinosaurs')
snapshot = ref.order_by_key().get()
print(snapshot)
Iść
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)

Zamawianie według wartości

Możesz uporządkować węzły według wartości ich kluczy podrzędnych, korzystając z metody orderByValue() . Załóżmy, że dinozaury biorą udział w zawodach sportowych dinozaurów, a Ty śledzisz ich wyniki w następującym formacie:

{
  "scores": {
    "bruhathkayosaurus" : 55,
    "lambeosaurus" : 21,
    "linhenykus" : 80,
    "pterodactyl" : 93,
    "stegosaurus" : 5,
    "triceratops" : 22
  }
}

Aby posortować dinozaury według ich wyniku, możesz skonstruować następujące zapytanie:

Jawa
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());
  });
});
Pyton
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))
Iść
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)
}

Zobacz sekcję Jak uporządkowane są dane , aby uzyskać wyjaśnienie sposobu sortowania wartości null , boolean, string i obiektu podczas korzystania z orderByValue() .

Złożone zapytania

Teraz, gdy jest już jasne, w jaki sposób uporządkowane są dane, możesz użyć opisanych poniżej metod ograniczania lub zakresu , aby konstruować bardziej złożone zapytania.

Ogranicz zapytania

Zapytania limitToFirst() i limitToLast() służą do ustawienia maksymalnej liczby dzieci, które mają być synchronizowane dla danego wywołania zwrotnego. Jeśli ustawisz limit na 100, początkowo będziesz otrzymywać tylko do 100 zdarzeń child_added . Jeśli w bazie danych masz mniej niż 100 wiadomości, dla każdej wiadomości zostanie uruchomione zdarzenie child_added . Jeśli jednak masz ponad 100 wiadomości, otrzymasz zdarzenie child_added tylko dla 100 z tych wiadomości. To jest pierwszych 100 uporządkowanych wiadomości, jeśli używasz limitToFirst() lub ostatnie 100 uporządkowanych wiadomości, jeśli używasz limitToLast() . Gdy elementy się zmienią, będziesz otrzymywać zdarzenia child_added dla elementów, które weszły do ​​zapytania i zdarzenia child_removed dla elementów, które je opuszczają, tak że łączna liczba pozostanie na poziomie 100.

Korzystając z bazy danych faktów o dinozaurach i orderByChild() , możesz znaleźć dwa najcięższe dinozaury:

Jawa
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);
});
Pyton
ref = db.reference('dinosaurs')
snapshot = ref.order_by_child('weight').limit_to_last(2).get()
for key in snapshot:
    print(key)
Iść
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 wyzwalane dokładnie dwa razy, chyba że w bazie danych znajdują się mniej niż dwa dinozaury. Zostanie również zwolniony za każdego nowego, cięższego dinozaura, który zostanie dodany do bazy danych. W Pythonie zapytanie bezpośrednio zwraca OrderedDict zawierający dwa najcięższe dinozaury.

Podobnie możesz znaleźć dwa najkrótsze dinozaury, używając limitToFirst() :

Jawa
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);
});
Pyton
ref = db.reference('dinosaurs')
snapshot = ref.order_by_child('height').limit_to_first(2).get()
for key in snapshot:
    print(key)
Iść
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 wyzwalane dokładnie dwa razy, chyba że w bazie danych znajdują się mniej niż dwa dinozaury. Zostanie również ponownie zwolniony, jeśli jeden z dwóch pierwszych dinozaurów zostanie usunięty z bazy danych, ponieważ nowy dinozaur będzie teraz drugim najniższym. W Pythonie zapytanie bezpośrednio zwraca OrderedDict zawierający najkrótsze dinozaury.

Możesz także przeprowadzać zapytania ograniczające za pomocą orderByValue() . Jeśli chcesz utworzyć tabelę liderów zawierającą 3 dinozaury sportowe o najwyższych wynikach, możesz wykonać następujące czynności:

Jawa
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());
  });
});
Pyton
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))
Iść
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 o zakres

Użycie startAt() , endAt() i equalTo() umożliwia wybranie dowolnego punktu początkowego i końcowego dla zapytań. Na przykład, jeśli chcesz znaleźć wszystkie dinozaury, które mają co najmniej trzy metry wzrostu, możesz połączyć orderByChild() i startAt() :

Jawa
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);
});
Pyton
ref = db.reference('dinosaurs')
snapshot = ref.order_by_child('height').start_at(3).get()
for key in snapshot:
    print(key)
Iść
ref := client.NewRef("dinosaurs")

results, err := ref.OrderByChild("height").StartAt(3).GetOrdered(ctx)
if err != nil {
	log.Fatalln("Error querying database:", err)
}
for _, r := range results {
	fmt.Println(r.Key())
}

Możesz użyć endAt() , aby znaleźć wszystkie dinozaury, których nazwy leksykograficznie występują przed Pterodaktylem:

Jawa
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);
});
Pyton
ref = db.reference('dinosaurs')
snapshot = ref.order_by_key().end_at('pterodactyl').get()
for key in snapshot:
    print(key)
Iść
ref := client.NewRef("dinosaurs")

results, err := ref.OrderByKey().EndAt("pterodactyl").GetOrdered(ctx)
if err != nil {
	log.Fatalln("Error querying database:", err)
}
for _, r := range results {
	fmt.Println(r.Key())
}

Możesz połączyć startAt() i endAt() , aby ograniczyć oba końce zapytania. Poniższy przykład wyszukuje wszystkie dinozaury, których nazwa zaczyna się na literę „b”:

Jawa
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);
});
Pyton
ref = db.reference('dinosaurs')
snapshot = ref.order_by_key().start_at('b').end_at(u'b\uf8ff').get()
for key in snapshot:
    print(key)
Iść
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 dokładnych dopasowań. Podobnie jak w przypadku innych zapytań dotyczących zakresu, zostanie ono uruchomione dla każdego pasującego węzła podrzędnego. Na przykład możesz użyć następującego zapytania, aby znaleźć wszystkie dinozaury o wysokości 25 metrów:

Jawa
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);
});
Pyton
ref = db.reference('dinosaurs')
snapshot = ref.order_by_child('height').equal_to(25).get()
for key in snapshot:
    print(key)
Iść
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 o zakres są również przydatne, gdy trzeba paginować dane.

Kładąc wszystko razem

Wszystkie te techniki można łączyć w celu tworzenia złożonych zapytań. Na przykład możesz znaleźć nazwę dinozaura, która jest nieco krótsza niż Stegozaur:

Jawa
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');
      }
    });
});
Pyton
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')
Iść
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")
}

Jak dane są uporządkowane

W tej sekcji wyjaśniono, w jaki sposób uporządkowane są dane podczas korzystania z każdej z czterech funkcji porządkowania.

zamówieniePrzezDziecko

Podczas korzystania z orderByChild() dane zawierające określony klucz podrzędny są porządkowane w następujący sposób:

  1. Dzieci z wartością null dla określonego klucza podrzędnego są traktowane jako pierwsze.
  2. Następne są dzieci z wartością false dla określonego klucza podrzędnego. Jeśli wiele elementów podrzędnych ma wartość false , są one sortowane leksykograficznie według klucza.
  3. Następne są dzieci z wartością true dla określonego klucza podrzędnego. Jeśli wiele elementów podrzędnych ma wartość true , są one sortowane leksykograficznie według klucza.
  4. Następne są dzieci z wartością liczbową, posortowane w kolejności rosnącej. Jeśli wiele elementów podrzędnych ma tę samą wartość liczbową dla określonego węzła podrzędnego, są one sortowane według klucza.
  5. Ciągi znaków występują po liczbach i są sortowane leksykograficznie w kolejności rosnącej. Jeśli wiele elementów podrzędnych ma tę samą wartość dla określonego węzła podrzędnego, są one uporządkowane leksykograficznie według klucza.
  6. Obiekty znajdują się na końcu i są sortowane leksykograficznie według kluczy w porządku rosnącym.

zamówienieByKey

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

  1. Na pierwszym miejscu znajdują się dzieci z kluczem, który można przeanalizować jako 32-bitową liczbę całkowitą, posortowane w kolejności rosnącej.
  2. Następne są dzieci, których kluczem jest wartość ciągu, posortowane leksykograficznie w kolejności rosnącej.

zamów według wartości

Podczas korzystania z orderByValue() elementy podrzędne są porządkowane według ich wartości. Kryteria porządkowania są takie same jak w przypadku orderByChild() , z tą różnicą, że zamiast wartości określonego klucza podrzędnego używana jest wartość węzła.