Recupero dei dati in corso...

Questo documento illustra le nozioni di base del recupero dei dati del database, dell'ordine dei dati e delle prestazioni semplici query sui dati. Il recupero dei dati in SDK Admin viene implementato in modo leggermente diverso a seconda del caso linguaggi di programmazione.

  1. Listener asincroni: I dati archiviati in Firebase Realtime Database vengono recuperati collegando un listener asincrono a un riferimento del database. Il listener viene attivato una volta per lo stato iniziale dei dati e ogni volta che i dati cambiano. Un gestore di eventi può ricevere diversi tipi di eventi. Questa modalità di recupero dei dati è supportata negli SDK amministrativi Java, Node.js e Python.
  2. Il blocco delle letture: I dati archiviati in Firebase Realtime Database vengono recuperati richiamando un metodo di blocco su un database che restituisce i dati archiviati nel riferimento. Ogni chiamata al metodo è una richiesta operativa. Ciò significa che l'SDK non registra alcun callback che ascolta i successivi aggiornamenti dei dati. Questo modello di recupero dei dati è supportato negli SDK amministrativi Python e Go.

Per iniziare

Rivediamo l'esempio di blogging dell'articolo precedente per capire come leggere i dati da un database Firebase. Richiamo che i post del blog nell'app di esempio siano archiviati nell'URL del database https://docs-examples.firebaseio.com/server/saving-data/fireblog/posts.json. Per leggere i dati dei tuoi post, puoi:

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())
Vai
// 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)
}

Se esegui il codice riportato sopra, vedrai un oggetto contenente tutti i tuoi post registrati nella console. In caso di Node.js e Java, la funzione listener viene chiamata ogni volta che vengono aggiunti nuovi dati al riferimento del database e non è necessario scrivere altro codice.

In Java e Node.js, la funzione di callback riceve un DataSnapshot, ovvero uno snapshot dei dati. Uno snapshot è un'immagine dei dati in un determinato riferimento di database in un singolo momento. La chiamata di val() / getValue() su uno snapshot restituisce la rappresentazione di un oggetto specifica per la lingua dei dati. Se non esistono dati nella posizione del riferimento, il valore dello snapshot è null. Il metodo get() in Python restituisce una rappresentazione Python dei dati direttamente. La funzione Get() in Go esegue il deprovisioning dei dati in una determinata struttura di dati.

Tieni presente che nell'esempio riportato sopra abbiamo utilizzato il tipo di evento value, che legge l'intero contenuto di un riferimento a un database Firebase, anche se è stato modificato un solo dato. value è uno dei cinque diversi tipi di eventi elencati di seguito che puoi utilizzare per leggere i dati dal database.

Leggi i tipi di eventi in Java e Node.js

Valore

L'evento value viene utilizzato per leggere uno snapshot statico dei contenuti in un determinato percorso del database, così come esistevano al momento dell'evento di lettura. Viene attivata una volta con i dati iniziali e di nuovo ogni volta che i dati cambiano. Il callback dell'evento viene trasmesso come uno snapshot contenente tutti i dati nella posizione in questione, inclusi i dati secondari. Nel codice di esempio riportato sopra, value ha restituito tutti i post del blog nella tua app. Ogni volta che viene aggiunto un nuovo post del blog, la funzione di callback restituisce tutti i post.

Bambino aggiunto

In genere l'evento child_added viene utilizzato quando si recupera un elenco di elementi dal database. A differenza di value che restituisce tutti i contenuti della località, child_added viene attivato una volta per ogni asset secondario esistente e poi di nuovo ogni volta che viene aggiunto un nuovo asset secondario al percorso specificato. Al callback dell'evento viene passato uno snapshot contenente i dati del nuovo bambino. Ai fini dell'ordinamento, viene passato anche un secondo argomento contenente la chiave del figlio precedente.

Se vuoi recuperare solo i dati di ogni nuovo post aggiunto alla tua app di blogging, puoi utilizzare 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);
});

In questo esempio, l'istantanea conterrà un oggetto con un singolo post del blog. Poiché l'SDK converte i post in oggetti recuperando il valore, puoi accedere alle proprietà dell'autore e del titolo del post chiamando rispettivamente author e title. Hai anche accesso all'ID post precedente dal secondo argomento prevChildKey.

Bambino modificato

L'evento child_changed viene attivato ogni volta che un nodo figlio viene modificato. Ciò include qualsiasi modifiche ai discendenti del nodo figlio. Viene in genere utilizzato in combinazione con child_added e child_removed per rispondere alle modifiche a un elenco di elementi. Lo snapshot passato al callback dell'evento contiene i dati aggiornati per il bucket secondario.

Puoi utilizzare child_changed per leggere i dati aggiornati nei post del blog quando vengono modificati:

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);
});

Bambino rimosso

L'evento child_removed viene attivato quando viene rimosso un elemento secondario immediato. In genere viene utilizzato insieme a child_added e child_changed. Lo snapshot passato al callback dell'evento contiene i dati relativi all'elemento secondario rimosso.

Nell'esempio del blog, puoi utilizzare child_removed per registrare nella console una notifica relativa al post eliminato:

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');
});

Bambino spostato

L'evento child_moved viene utilizzato quando si lavora con dati ordinati, come descritto nella sezione successiva.

Garanzie relative agli eventi

Il database Firebase offre diverse importanti garanzie in merito agli eventi:

Garanzie degli eventi del database
Gli eventi verranno sempre attivati quando lo stato locale cambia.
Alla fine, gli eventi rifletteranno sempre lo stato corretto dei dati, anche nei casi in cui le operazioni locali o causano differenze temporanee, ad esempio la perdita temporanea della connessione di rete.
Le scritture da un singolo client verranno sempre scritte sul server e trasmesse in sequenza agli altri utenti.
Gli eventi Value vengono sempre attivati per ultimi e contengono sempre aggiornamenti di tutti gli altri eventi che si sono verificati prima dell'acquisizione dell'istantanea.

Poiché gli eventi valore vengono sempre attivati per ultimi, l'esempio seguente funzionerà sempre:

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);
});

Scollegamento dei callback

I callback vengono rimossi specificando il tipo di evento e la funzione di callback da rimuovere, come di seguito:

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

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

Se hai passato un contesto di ambito a on(), questo deve essere passato quando scolleghi il callback:

Java
// Not applicable for Java
Node.js
ref.off('value', originalCallback, ctx);

Se vuoi rimuovere tutte le chiamate a una determinata località, procedi nel seguente modo:

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

Lettura dei dati una volta

In alcuni casi può essere utile che una richiamata venga chiamata una volta e poi venga rimossa immediatamente. Abbiamo creato una funzione di supporto per semplificare la procedura:

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())
Vai
// 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)
}

Esecuzione di query sui dati

Con le query sui database di Firebase, puoi recuperare selettivamente i dati in base a vari fattori. Per creare una query nel tuo database, inizia specificando la modalità di ordinamento dei dati utilizzando una delle funzioni di ordinamento: orderByChild(), orderByKey() o orderByValue(). Puoi quindi combinare questi elementi con altri cinque metodi per eseguire query complesse: limitToFirst(), limitToLast(), startAt(), endAt() e equalTo().

Poiché tutti noi di Firebase pensiamo che i dinosauri siano davvero fantastici, utilizzeremo uno snippet da un database di esempio di informazioni sui dinosauri per dimostrare come eseguire query sui dati nel tuo database Firebase.:

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

Puoi ordinare i dati in tre modi: per chiave secondaria, per chiave o per valore. Una query di database di base inizia con una di queste funzioni di ordinamento, ognuna delle quali è descritta di seguito.

Ordine in base a una chiave figlio specificata

Puoi ordinare i nodi in base a una chiave figlio comune passando questa chiave a orderByChild(). Ad esempio, per leggere tutti i dinosauri ordinati per altezza, puoi fare le seguenti:

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))
Vai
// 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)
}

Tutti i nodi privi della chiave figlio su cui stiamo eseguendo la query vengono ordinati con il valore null, il che significa che verrà inserito per primo nell'ordine. Per informazioni dettagliate su come vengono ordinati i dati, consulta la sezione Come vengono ordinati i dati.

Le query possono anche essere ordinate in base a elementi secondari profondamente nidificati, anziché solo in base a elementi secondari di un livello inferiore. È utile se i dati sono molto nidificati in questo modo:

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

Per eseguire query sull'altezza ora, puoi utilizzare il percorso completo dell'oggetto anziché una singola chiave:

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))
Vai
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)
}

Le query possono essere ordinate solo per una chiave alla volta. Chiamata a orderByChild() più volte per la stessa query genera un errore.

Ordine per chiave

Puoi anche ordinare i nodi in base alle relative chiavi utilizzando il metodo orderByKey(). L'esempio seguente legge tutti i dinosauri in ordine alfabetico:

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)
Vai
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)

Ordine per valore

Puoi ordinare i nodi in base al valore delle chiavi figlio utilizzando il metodo orderByValue(). Supponiamo che i dinosauri partecipino a una competizione sportiva e che tu riesca a tenere traccia dei loro punteggi nel seguente formato:

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

Per ordinare i dinosauri in base al punteggio, puoi creare la seguente query:

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))
Vai
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)
}

Consulta la sezione Come vengono ordinati i dati per una spiegazione su come i valori di null, booleani, stringa e oggetto vengono ordinati quando si utilizza orderByValue().

Query complesse

Ora che è chiaro come vengono ordinati i dati, puoi utilizzare i metodi limite o intervallo descritti di seguito per creare query più complesse.

Limita query

Le query limitToFirst() e limitToLast() vengono utilizzate per impostare numero massimo di elementi secondari da sincronizzare per un determinato callback. Se imposti un limite di 100, inizialmente riceverai solo fino a 100 eventi child_added. Se hai meno di 100 messaggi archiviati nel database, verrà attivato un evento child_added per ogni per creare un nuovo messaggio email. Tuttavia, se hai più di 100 messaggi, riceverai solo un child_added per 100 di questi messaggi. Questi sono i primi 100 messaggi ordinati se utilizzi limitToFirst() o gli ultimi 100 messaggi ordinati se utilizzi limitToLast(). Quando gli elementi cambiano, riceverai eventi child_added per gli elementi che entrano nella query ed eventi child_removed per quelli che la escono, in modo che il numero totale rimanga 100.

Utilizzando il database dei fatti sui dinosauri e orderByChild(), puoi trovare due risposte dinosauri:

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)
Vai
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())
}

Il callback child_added viene attivato esattamente due volte, a meno che non ci siano nel database sono archiviati meno di due dinosauri. Verrà attivato anche per ogni nuovo dinosauro più pesante aggiunto al database. In Python, la query restituisce direttamente un valore OrderedDict contenente i due dinosauri più pesanti.

Allo stesso modo, puoi trovare i due dinosauri più piccoli utilizzando 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)
Vai
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())
}

Il callback child_added viene attivato esattamente due volte, a meno che nel database non siano archiviati meno di due dinosauri. Inoltre, verrà licenziato di nuovo se uno dei primi due dinosauri viene rimosso dal database, dato che un nuovo dinosauro sarà il secondo più corto. In Python, la query restituisce direttamente un OrderedDict contenente i dinosauri più piccoli.

Puoi anche eseguire query con limiti con orderByValue(). Se vuoi creare una classifica con i 3 dinosauri con i punteggi più alti, puoi procedere nel seguente modo:

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))
Vai
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)
}

Query sull'intervallo

L'uso di startAt(), endAt() e equalTo() ti consente di: scegliere punti di partenza e di arrivo arbitrari per le query. Ad esempio, se volessi trovare tutti i dinosauri alti almeno tre metri, puoi combinare orderByChild() e 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)
Vai
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())
}

Puoi utilizzare endAt() per trovare tutti i dinosauri il cui nome precede Pterodattilo lessicograficamente:

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)
Vai
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())
}

Puoi combinare startAt() e endAt() per limitare entrambe le estremità della tua query. Il seguente esempio trova tutti i dinosauri il cui nome inizia con la lettera "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)
Vai
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())
}

Il metodo equalTo() consente di filtrare in base alle corrispondenze esatte. Così come avviene con le altre query di intervallo, si attiverà per ogni nodo figlio corrispondente. Ad esempio, puoi utilizza la seguente query per trovare tutti i dinosauri che sono alti 25 metri:

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)
Vai
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())
}

Le query sugli intervalli sono utili anche quando devi impaginare i dati.

Riepilogo

Puoi combinare tutte queste tecniche per creare query complesse. Ad esempio, puoi trovare il nome del dinosauro che è solo più corto dello Stegosaurus:

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')
Vai
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")
}

Come vengono ordinati i dati

Questa sezione spiega come vengono ordinati i dati quando si utilizzano ciascuna delle quattro funzioni di ordinamento.

ordineByChild

Quando utilizzi orderByChild(), i dati contenenti la chiave secondaria specificata vengono ordinati come segue:

  1. I figli con un valore null per la chiave secondaria specificata vengono visualizzati per primi.
  2. Gli elementi secondari con un valore pari a false per la chiave secondaria specificata vengono visualizzati dopo. Se più elementi secondari hanno un valore false, vengono ordinati lessicograficamente per chiave.
  3. Seguono gli elementi secondari con un valore di true per la chiave secondaria specificata. Se più elementi secondari hanno un valore true, vengono ordinati in ordine alfabetico per chiave.
  4. Gli elementi secondari con un valore numerico vengono visualizzati dopo, in ordine crescente. Se più elementi secondari hanno lo stesso valore numerico per il nodo secondario specificato, vengono ordinati in base alla chiave.
  5. Le stringhe vengono dopo i numeri e sono ordinate lessicograficamente in ordine crescente. Se più nodi figlio hanno lo stesso valore per il nodo figlio specificato, questi vengono ordinati lessicograficamente per chiave.
  6. Gli oggetti sono gli ultimi e sono ordinati lessicograficamente per chiave in ordine crescente.

OrdinePerChiave

Quando utilizzi orderByKey() per ordinare i dati, i dati vengono restituiti in ordine crescente per chiave come segue. Tieni presente che le chiavi possono essere solo stringhe.

  1. Gli elementi secondari con una chiave analizzabile come numero intero a 32 bit vengono visualizzati per primi e ordinati in ordine crescente.
  2. Figli con un valore stringa come chiave successivo, ordinati lessicograficamente in ordine crescente.

OrdinePerValore

Quando utilizzi orderByValue(), gli elementi secondari vengono ordinati in base al valore. I criteri di ordinamento sono gli stessi di orderByChild(), ad eccezione del fatto che viene utilizzato il valore del nodo anziché il valore di una chiave figlio specificata.