Recupero dati

Questo documento tratta le nozioni di base sul recupero dei dati del database, su come i dati vengono ordinati e su come eseguire semplici query sui dati. Il recupero dei dati nell'Admin SDK viene implementato in modo leggermente diverso nei diversi linguaggi di programmazione.

  1. Listener asincroni: i dati archiviati in un Firebase Realtime Database vengono recuperati collegando un ascoltatore asincrono a un riferimento al database. Il listener viene attivato una volta per lo stato iniziale dei dati e nuovamente ogni volta che i dati cambiano. Un ascoltatore di eventi può ricevere diversi tipi di eventi . Questa modalità di recupero dei dati è supportata negli SDK di amministrazione Java, Node.js e Python.
  2. Letture bloccanti: i dati archiviati in un Firebase Realtime Database vengono recuperati invocando un metodo di blocco su un riferimento al database, che restituisce i dati archiviati nel riferimento. Ogni chiamata al metodo è un'operazione una tantum. Ciò significa che l'SDK non registra alcun callback in ascolto dei successivi aggiornamenti dei dati. Questo modello di recupero dei dati è supportato negli SDK Python e Go Admin.

Iniziare

Rivisitiamo l'esempio di blog dell'articolo precedente per capire come leggere i dati da un database Firebase. Ricorda che i post del blog nell'app di esempio sono archiviati nell'URL del database https://docs-examples.firebaseio.com/server/ saving-data/fireblog/posts.json . Per leggere i dati del tuo post, puoi procedere come segue:

Giava
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);
}); 
Pitone
# 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())
Andare

// 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 precedente, vedrai un oggetto contenente tutti i tuoi post registrati sulla console. Nel 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 alcun codice aggiuntivo per far sì che ciò accada.

In Java e Node.js, la funzione di callback riceve un DataSnapshot , che è un'istantanea dei dati. Uno snapshot è un'immagine dei dati in un particolare riferimento al database in un singolo momento. La chiamata a val() / getValue() su uno snapshot restituisce la rappresentazione di un oggetto specifico della lingua dei dati. Se non esistono dati nella posizione del riferimento, il valore dello snapshot è null . Il metodo get() in Python restituisce direttamente una rappresentazione Python dei dati. La funzione Get() in Go annulla il marshalling dei dati in una determinata struttura dati.

Tieni presente che nell'esempio precedente abbiamo utilizzato il tipo di evento value , che legge l'intero contenuto di un riferimento al database Firebase, anche se è stato modificato solo un dato. value è uno dei cinque diversi tipi di eventi elencati di seguito che è possibile 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 un'istantanea statica dei contenuti in un determinato percorso del database, così come esistevano al momento dell'evento di lettura. Viene attivato una volta con i dati iniziali e di nuovo ogni volta che i dati cambiano. Al callback dell'evento viene passata uno snapshot contenente tutti i dati in quella posizione, inclusi i dati figlio. Nell'esempio di codice precedente, value ha restituito tutti i post del blog nella tua app. Ogni volta che viene aggiunto un nuovo post sul blog, la funzione di callback restituirà tutti i post.

Bambino aggiunto

L'evento child_added viene in genere utilizzato quando si recupera un elenco di elementi dal database. A differenza di value che restituisce l'intero contenuto della posizione, child_added viene attivato una volta per ogni figlio esistente e poi di nuovo ogni volta che un nuovo figlio viene aggiunto al percorso specificato. Al callback dell'evento viene passata un'istantanea contenente i dati del nuovo figlio. Ai fini dell'ordinamento viene passato anche un secondo argomento contenente la chiave del figlio precedente.

Se desideri recuperare solo i dati su ogni nuovo post aggiunto alla tua app di blogging, puoi utilizzare child_added :

Giava
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 lo snapshot conterrà un oggetto con un singolo post del blog. Poiché l'SDK converte i post in oggetti recuperando il valore, hai accesso alle proprietà dell'autore e del titolo del post chiamando rispettivamente author e title . Hai anche accesso all'ID del post precedente dal secondo argomento prevChildKey .

Bambino cambiato

L'evento child_changed viene attivato ogni volta che un nodo figlio viene modificato. Ciò include qualsiasi modifica ai discendenti del nodo figlio. Viene generalmente utilizzato insieme a 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 bambino.

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

Giava
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 figlio immediato. Viene generalmente utilizzato insieme child_added e child_changed . Lo snapshot passato al callback dell'evento contiene i dati per il figlio rimosso.

Nell'esempio del blog, puoi utilizzare child_removed per registrare una notifica sul post eliminato sulla console:

Giava
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 sugli eventi

Il database Firebase fornisce diverse garanzie importanti relative agli eventi:

Garanzie sugli eventi del database
Gli eventi verranno sempre attivati ​​quando cambia lo stato locale.
Alla fine gli eventi rifletteranno sempre lo stato corretto dei dati, anche nei casi in cui le operazioni o le tempistiche locali causano differenze temporanee, come nella perdita temporanea della connessione di rete.
Le scritture da un singolo client verranno sempre scritte sul server e trasmesse agli altri utenti in ordine.
Gli eventi valore vengono sempre attivati ​​per ultimi e contengono sicuramente aggiornamenti da qualsiasi altro evento verificatosi prima dell'acquisizione dello snapshot.

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

Giava
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 delle richiamate

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

Giava
// 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() , deve essere passato quando scolleghi il callback:

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

Se desideri rimuovere tutte le richiamate in una posizione, puoi procedere come segue:

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

Leggere i dati una volta

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

Giava
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
});
Pitone
# 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())
Andare
// 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)
}

Interrogazione dei dati

Con le query del database Firebase, puoi recuperare selettivamente i dati in base a vari fattori. Per costruire una query nel tuo database, inizi specificando come vuoi che i tuoi dati siano ordinati utilizzando una delle funzioni di ordinamento: orderByChild() , orderByKey() o orderByValue() . È quindi possibile combinarli con altri cinque metodi per condurre query complesse: limitToFirst() , limitToLast() , startAt() , endAt() e equalTo() .

Poiché tutti noi di Firebase pensiamo che i dinosauri siano piuttosto interessanti, utilizzeremo uno snippet da un database di esempio di fatti sui dinosauri per dimostrare come è possibile eseguire query sui dati nel 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 figlio , per chiave o per valore . Una query di database di base inizia con una di queste funzioni di ordinamento, ciascuna delle quali è spiegata di seguito.

Ordinamento in base a una chiave figlio specificata

Puoi ordinare i nodi in base a una chiave figlio comune passando quella chiave a orderByChild() . Ad esempio, per leggere tutti i dinosauri ordinati per altezza, puoi procedere come segue:

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

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

Qualsiasi nodo che non ha la chiave figlio su cui stiamo interrogando viene ordinato con un valore null , il che significa che verrà primo nell'ordine. Per dettagli su come vengono ordinati i dati, vedere la sezione Come vengono ordinati i dati .

Le query possono anche essere ordinate in base ai figli profondamente annidati, anziché solo ai figli di un livello inferiore. Questo è utile se disponi di dati profondamente annidati come questi:

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

Per interrogare l'altezza ora, puoi utilizzare il percorso completo dell'oggetto anziché una singola chiave:

Giava
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');
});
Pitone
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))
Andare
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. Chiamare orderByChild() più volte sulla stessa query genera un errore.

Ordinazione per chiave

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

Giava
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);
});
Pitone
ref = db.reference('dinosaurs')
snapshot = ref.order_by_key().get()
print(snapshot)
Andare
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)

Ordinamento per valore

Puoi ordinare i nodi in base al valore delle loro chiavi figlio utilizzando il metodo orderByValue() . Supponiamo che i dinosauri stiano partecipando a una competizione sportiva sui dinosauri e tu stia tenendo 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 loro punteggio, potresti costruire la seguente query:

Giava
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());
  });
});
Pitone
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))
Andare
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 vengono ordinati i valori null , booleani, stringa e oggetto quando si utilizza orderByValue() .

Domande complesse

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

Limita le query

Le query limitToFirst() e limitToLast() vengono utilizzate per impostare un numero massimo di figli 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 tuo database, verrà attivato un evento child_added per ciascun messaggio. Tuttavia, se hai più di 100 messaggi, riceverai solo un evento 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() . Man mano che gli elementi cambiano, riceverai eventi child_added per gli elementi che entrano nella query ed eventi child_removed per gli elementi che la lasciano, in modo che il numero totale rimanga a 100.

Utilizzando il database dei fatti sui dinosauri e orderByChild() , puoi trovare i due dinosauri più pesanti:

Giava
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);
});
Pitone
ref = db.reference('dinosaurs')
snapshot = ref.order_by_child('weight').limit_to_last(2).get()
for key in snapshot:
    print(key)
Andare
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())
}

La richiamata child_added viene attivata esattamente due volte, a meno che nel database non siano memorizzati meno di due dinosauri. Verrà inoltre attivato per ogni nuovo dinosauro più pesante che verrà aggiunto al database. In Python, la query restituisce direttamente un OrderedDict contenente i due dinosauri più pesanti.

Allo stesso modo, puoi trovare i due dinosauri più bassi usando limitToFirst() :

Giava
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);
});
Pitone
ref = db.reference('dinosaurs')
snapshot = ref.order_by_child('height').limit_to_first(2).get()
for key in snapshot:
    print(key)
Andare
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())
}

La richiamata child_added viene attivata esattamente due volte, a meno che nel database non siano memorizzati meno di due dinosauri. Verrà licenziato di nuovo anche se uno dei primi due dinosauri viene rimosso dal database, poiché un nuovo dinosauro sarà ora il secondo più basso. In Python, la query restituisce direttamente un OrderedDict contenente i dinosauri più bassi.

Puoi anche condurre query sui limiti con orderByValue() . Se vuoi creare una classifica con i primi 3 dinosauri dei dinosauri con il punteggio più alto, puoi fare quanto segue:

Giava
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());
  });
});
Pitone
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))
Andare
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)
}

Intervallo di query

L'uso startAt() , endAt() e equalTo() ti consente di scegliere punti iniziali e finali arbitrari per le tue query. Ad esempio, se desideri trovare tutti i dinosauri alti almeno tre metri, puoi combinare orderByChild() e startAt() :

Giava
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);
});
Pitone
ref = db.reference('dinosaurs')
snapshot = ref.order_by_child('height').start_at(3).get()
for key in snapshot:
    print(key)
Andare
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 usare endAt() per trovare tutti i dinosauri i cui nomi precedono lessicograficamente Pterodactyl:

Giava
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);
});
Pitone
ref = db.reference('dinosaurs')
snapshot = ref.order_by_key().end_at('pterodactyl').get()
for key in snapshot:
    print(key)
Andare
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 query. L'esempio seguente trova tutti i dinosauri il cui nome inizia con la lettera "b":

Giava
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);
});
Pitone
ref = db.reference('dinosaurs')
snapshot = ref.order_by_key().start_at('b').end_at(u'b\uf8ff').get()
for key in snapshot:
    print(key)
Andare
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. Come nel caso delle altre query di intervallo, verrà attivato per ciascun nodo figlio corrispondente. Ad esempio, puoi utilizzare la seguente query per trovare tutti i dinosauri alti 25 metri:

Giava
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);
});
Pitone
ref = db.reference('dinosaurs')
snapshot = ref.order_by_child('height').equal_to(25).get()
for key in snapshot:
    print(key)
Andare
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 su intervalli sono utili anche quando è necessario impaginare i dati.

Mettere tutto insieme

È possibile combinare tutte queste tecniche per creare query complesse. Ad esempio, puoi trovare il nome del dinosauro che è appena più corto di Stegosaurus:

Giava
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');
      }
    });
});
Pitone
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')
Andare
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 utilizza ciascuna delle quattro funzioni di ordinamento.

ordineByChild

Quando si utilizza orderByChild() , i dati che contengono la chiave figlio specificata vengono ordinati come segue:

  1. I bambini con un valore null per la chiave figlio specificata vengono prima.
  2. I figli con un valore false per la chiave figlio specificata vengono dopo. Se più figli hanno un valore false , vengono ordinati lessicograficamente per chiave.
  3. I figli con un valore true per la chiave figlio specificata vengono dopo. Se più figli hanno un valore true , vengono ordinati lessicograficamente per chiave.
  4. Seguono i bambini con un valore numerico, ordinati in ordine crescente. Se più figli hanno lo stesso valore numerico per il nodo figlio specificato, vengono ordinati per chiave.
  5. Le stringhe vengono dopo i numeri e sono ordinate lessicograficamente in ordine crescente. Se più figli hanno lo stesso valore per il nodo figlio specificato, vengono ordinati lessicograficamente per chiave.
  6. Gli oggetti vengono per ultimi e ordinati lessicograficamente per chiave in ordine crescente.

ordinePerChiave

Quando si utilizza 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. I figli con una chiave che può essere analizzata come intero a 32 bit vengono prima, ordinati in ordine crescente.
  2. Seguono i bambini con un valore stringa come chiave, ordinati lessicograficamente in ordine crescente.

ordinePerValore

Quando si utilizza orderByValue() , i bambini vengono ordinati in base al loro valore. Il criterio di ordinamento è lo stesso di orderByChild() , tranne per il fatto che viene utilizzato il valore del nodo invece del valore di una chiave figlio specificata.