Récupération des données

Restez organisé à l'aide des collections Enregistrez et classez les contenus selon vos préférences.

Ce document couvre les bases de la récupération des données de la base de données, la manière dont les données sont triées et la manière d'effectuer des requêtes simples sur les données. La récupération des données dans le SDK Admin est implémentée légèrement différemment selon les langages de programmation.

  1. Écouteurs asynchrones : les données stockées dans une base de données en temps réel Firebase sont récupérées en attachant un écouteur asynchrone à une référence de base de données. L'écouteur est déclenché une fois pour l'état initial des données et à nouveau chaque fois que les données changent. Un écouteur d'événements peut recevoir plusieurs types d'événements différents. Ce mode de récupération de données est pris en charge dans les SDK Java, Node.js et Python Admin.
  2. Blocage des lectures : les données stockées dans une base de données en temps réel Firebase sont récupérées en appelant une méthode de blocage sur une référence de base de données, qui renvoie les données stockées au niveau de la référence. Chaque appel de méthode est une opération unique. Cela signifie que le SDK n'enregistre aucun rappel qui écoute les mises à jour de données ultérieures. Ce modèle de récupération de données est pris en charge dans les SDK Python et Go Admin.

Commencer

Reprenons l'exemple de blog de l'article précédent pour comprendre comment lire les données d'une base de données Firebase. Rappelez-vous que les articles de blog dans l'exemple d'application sont stockés à l'URL de la base de données https://docs-examples.firebaseio.com/server/saving-data/fireblog/posts.json . Pour lire vos données de publication, vous pouvez procéder comme suit :

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

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

Si vous exécutez le code ci-dessus, vous verrez un objet contenant tous vos messages connectés à la console. Dans le cas de Node.js et Java, la fonction d'écoute est appelée chaque fois que de nouvelles données sont ajoutées à votre référence de base de données, et vous n'avez pas besoin d'écrire de code supplémentaire pour que cela se produise.

Dans Java et Node.js, la fonction de rappel reçoit un DataSnapshot , qui est un instantané des données. Un instantané est une image des données d'une référence de base de données particulière à un moment donné. L'appel de val() / getValue() sur un instantané renvoie une représentation d'objet spécifique au langage des données. Si aucune donnée n'existe à l'emplacement de la référence, la valeur de l'instantané est null . La méthode get() en Python renvoie directement une représentation Python des données. La fonction Get() dans Go démarshale les données dans une structure de données donnée.

Notez que nous avons utilisé le type d'événement value dans l'exemple ci-dessus, qui lit tout le contenu d'une référence de base de données Firebase, même si une seule donnée a changé. value est l'un des cinq types d'événements différents répertoriés ci-dessous que vous pouvez utiliser pour lire les données de la base de données.

Lire les types d'événements en Java et Node.js

Évaluer

L'événement de value est utilisé pour lire un instantané statique du contenu d'un chemin de base de données donné, tel qu'il existait au moment de l'événement de lecture. Il est déclenché une fois avec les données initiales et à nouveau à chaque fois que les données changent. Le rappel d'événement reçoit un instantané contenant toutes les données à cet emplacement, y compris les données enfants. Dans l'exemple de code ci-dessus, value a renvoyé tous les articles de blog de votre application. Chaque fois qu'un nouveau billet de blog est ajouté, la fonction de rappel renverra tous les messages.

Enfant ajouté

L'événement child_added est généralement utilisé lors de la récupération d'une liste d'éléments de la base de données. Contrairement à value qui renvoie l'intégralité du contenu de l'emplacement, child_added est déclenché une fois pour chaque enfant existant, puis à chaque fois qu'un nouvel enfant est ajouté au chemin spécifié. Le rappel d'événement reçoit un instantané contenant les données du nouvel enfant. À des fins de classement, il est également passé un deuxième argument contenant la clé de l'enfant précédent.

Si vous souhaitez récupérer uniquement les données de chaque nouvelle publication ajoutée à votre application de blog, vous pouvez utiliser 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);
});

Dans cet exemple, l'instantané contiendra un objet avec un article de blog individuel. Étant donné que le SDK convertit les publications en objets en récupérant la valeur, vous avez accès aux propriétés d'auteur et de titre de la publication en appelant respectivement l' author et title . Vous avez également accès à l'ID de publication précédente à partir du deuxième argument prevChildKey .

Enfant modifié

L'événement child_changed est déclenché chaque fois qu'un nœud enfant est modifié. Cela inclut toutes les modifications apportées aux descendants du nœud enfant. Il est généralement utilisé en conjonction avec child_added et child_removed pour répondre aux modifications apportées à une liste d'éléments. L'instantané transmis au rappel d'événement contient les données mises à jour pour l'enfant.

Vous pouvez utiliser child_changed pour lire les données mises à jour sur les articles de blog lorsqu'ils sont modifiés :

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

Enfant supprimé

L'événement child_removed est déclenché lorsqu'un enfant immédiat est supprimé. Il est généralement utilisé en conjonction avec child_added et child_changed . L'instantané transmis au rappel d'événement contient les données de l'enfant supprimé.

Dans l'exemple de blog, vous pouvez utiliser child_removed pour consigner une notification concernant le message supprimé dans la console :

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

Enfant déplacé

L'événement child_moved est utilisé lorsque vous travaillez avec des données ordonnées, ce qui est couvert dans la section suivante .

Garanties d'événement

La base de données Firebase offre plusieurs garanties importantes concernant les événements :

Garanties d'événement de base de données
Les événements seront toujours déclenchés lorsque l'état local change.
Les événements finiront toujours par refléter l'état correct des données, même dans les cas où les opérations locales ou la synchronisation provoquent des différences temporaires, comme la perte temporaire de la connexion réseau.
Les écritures d'un seul client seront toujours écrites sur le serveur et diffusées aux autres utilisateurs dans l'ordre.
Les événements de valeur sont toujours déclenchés en dernier et sont garantis pour contenir les mises à jour de tout autre événement qui s'est produit avant la prise de cet instantané.

Étant donné que les événements de valeur sont toujours déclenchés en dernier, l'exemple suivant fonctionnera toujours :

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

Détacher les rappels

Les rappels sont supprimés en spécifiant le type d'événement et la fonction de rappel à supprimer, comme suit :

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

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

Si vous avez passé un contexte de portée dans on() , il doit être passé lors du détachement du rappel :

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

Si vous souhaitez supprimer tous les rappels à un emplacement, vous pouvez procéder comme suit :

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

Lire les données une fois

Dans certains cas, il peut être utile qu'un rappel soit appelé une fois, puis immédiatement supprimé. Nous avons créé une fonction d'assistance pour vous faciliter la tâche :

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

Interroger des données

Avec les requêtes de base de données Firebase, vous pouvez récupérer des données de manière sélective en fonction de divers facteurs. Pour construire une requête dans votre base de données, vous commencez par spécifier comment vous souhaitez que vos données soient triées à l'aide de l'une des fonctions de tri : orderByChild() , orderByKey() ou orderByValue() . Vous pouvez ensuite les combiner avec cinq autres méthodes pour effectuer des requêtes complexes : limitToFirst() , limitToLast() , startAt() , endAt() et equalTo() .

Étant donné que chez Firebase, nous pensons tous que les dinosaures sont plutôt cool, nous allons utiliser un extrait d'un exemple de base de données d'informations sur les dinosaures pour montrer comment vous pouvez interroger les données de votre base de données Firebase :

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

Vous pouvez trier les données de trois manières : par clé enfant , par clé ou par valeur . Une requête de base de données de base commence par l'une de ces fonctions de tri, chacune étant expliquée ci-dessous.

Trier par une clé enfant spécifiée

Vous pouvez ordonner les nœuds par une clé enfant commune en transmettant cette clé à orderByChild() . Par exemple, pour lire tous les dinosaures classés par taille, vous pouvez procéder comme suit :

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

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

Tout nœud qui n'a pas la clé enfant sur laquelle nous interrogeons est trié avec une valeur de null , ce qui signifie qu'il viendra en premier dans l'ordre. Pour plus de détails sur la façon dont les données sont triées, consultez la section Comment les données sont triées .

Les requêtes peuvent également être classées par enfants profondément imbriqués, plutôt que par des enfants d'un niveau inférieur uniquement. Ceci est utile si vous avez des données profondément imbriquées comme celle-ci :

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

Pour interroger la hauteur maintenant, vous pouvez utiliser le chemin complet vers l'objet plutôt qu'une seule clé :

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

Les requêtes ne peuvent être triées que par une clé à la fois. Appeler orderByChild() plusieurs fois sur la même requête génère une erreur.

Commande par clé

Vous pouvez également trier les nœuds par leurs clés à l'aide de la méthode orderByKey() . L'exemple suivant lit tous les dinosaures par ordre alphabétique :

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

Trier par valeur

Vous pouvez classer les nœuds par la valeur de leurs clés enfants à l'aide de la méthode orderByValue() . Supposons que les dinosaures organisent une compétition sportive de dino et que vous suiviez leurs scores au format suivant :

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

Pour trier les dinosaures selon leur score, vous pouvez construire la requête suivante :

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

Voir la section Comment les données sont triées pour une explication sur la façon dont les valeurs null , boolean, string et object sont triées lors de l'utilisation orderByValue() .

Requêtes complexes

Maintenant que vous savez clairement comment vos données sont ordonnées, vous pouvez utiliser les méthodes de limite ou de plage décrites ci-dessous pour construire des requêtes plus complexes.

Limiter les requêtes

Les limitToFirst() et limitToLast() permettent de définir un nombre maximum d'enfants à synchroniser pour un rappel donné. Si vous définissez une limite de 100, vous ne recevrez initialement que 100 événements child_added . Si vous avez moins de 100 messages stockés dans votre base de données, un événement child_added se déclenchera pour chaque message. Cependant, si vous avez plus de 100 messages, vous ne recevrez un événement child_added que pour 100 de ces messages. Ce sont les 100 premiers messages ordonnés si vous utilisez limitToFirst() ou les 100 derniers messages ordonnés si vous utilisez limitToLast() . Au fur et à mesure que les éléments changent, vous recevrez des événements child_added pour les éléments qui entrent dans la requête et des événements child_removed pour les éléments qui la quittent, de sorte que le nombre total reste à 100.

En utilisant la base de données d'informations sur les dinosaures et orderByChild() , vous pouvez trouver les deux dinosaures les plus lourds :

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

Le rappel child_added est déclenché exactement deux fois, sauf s'il y a moins de deux dinosaures stockés dans la base de données. Il sera également renvoyé pour chaque nouveau dinosaure plus lourd ajouté à la base de données. En Python, la requête renvoie directement un OrderedDict contenant les deux dinosaures les plus lourds.

De même, vous pouvez trouver les deux dinosaures les plus courts en utilisant 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)
Aller
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())
}

Le rappel child_added est déclenché exactement deux fois, sauf s'il y a moins de deux dinosaures stockés dans la base de données. Il sera également renvoyé si l'un des deux premiers dinosaures est supprimé de la base de données, car un nouveau dinosaure sera désormais le deuxième plus court. En Python, la requête renvoie directement un OrderedDict contenant les dinosaures les plus courts.

Vous pouvez également effectuer des requêtes de limite avec orderByValue() . Si vous souhaitez créer un classement avec les 3 dinosaures sportifs les plus performants, vous pouvez procéder comme suit :

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

Requêtes de plage

L'utilisation startAt() , endAt() et equalTo() vous permet de choisir des points de départ et d'arrivée arbitraires pour vos requêtes. Par exemple, si vous souhaitez trouver tous les dinosaures mesurant au moins trois mètres de haut, vous pouvez combiner orderByChild() et 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)
Aller
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())
}

Vous pouvez utiliser endAt() pour rechercher lexicographiquement tous les dinosaures dont les noms précèdent Ptérodactyle :

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

Vous pouvez combiner startAt() et endAt() pour limiter les deux extrémités de votre requête. L'exemple suivant trouve tous les dinosaures dont le nom commence par la lettre "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)
Aller
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())
}

La méthode equalTo() vous permet de filtrer en fonction des correspondances exactes. Comme c'est le cas avec les autres requêtes de plage, il se déclenchera pour chaque nœud enfant correspondant. Par exemple, vous pouvez utiliser la requête suivante pour trouver tous les dinosaures mesurant 25 mètres de haut :

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

Les requêtes de plage sont également utiles lorsque vous devez paginer vos données.

Mettre tous ensemble

Vous pouvez combiner toutes ces techniques pour créer des requêtes complexes. Par exemple, vous pouvez trouver le nom du dinosaure qui est juste plus court que 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')
Aller
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")
}

Comment les données sont ordonnées

Cette section explique comment vos données sont triées lors de l'utilisation de chacune des quatre fonctions de tri.

orderByChild

Lors de l'utilisation orderByChild() , les données contenant la clé enfant spécifiée sont triées comme suit :

  1. Les enfants avec une valeur null pour la clé enfant spécifiée viennent en premier.
  2. Les enfants avec une valeur false pour la clé enfant spécifiée viennent ensuite. Si plusieurs enfants ont la valeur false , ils sont triés lexicographiquement par clé.
  3. Les enfants avec une valeur true pour la clé enfant spécifiée viennent ensuite. Si plusieurs enfants ont la valeur true , ils sont triés lexicographiquement par clé.
  4. Les enfants avec une valeur numérique viennent ensuite, triés par ordre croissant. Si plusieurs enfants ont la même valeur numérique pour le nœud enfant spécifié, ils sont triés par clé.
  5. Les chaînes viennent après les nombres et sont triées lexicographiquement par ordre croissant. Si plusieurs enfants ont la même valeur pour le nœud enfant spécifié, ils sont triés lexicographiquement par clé.
  6. Les objets viennent en dernier et sont triés lexicographiquement par clé dans l'ordre croissant.

orderByKey

Lorsque vous utilisez orderByKey() pour trier vos données, les données sont renvoyées dans l'ordre croissant par clé comme suit. Gardez à l'esprit que les clés ne peuvent être que des chaînes.

  1. Les enfants avec une clé qui peut être analysée comme un entier 32 bits viennent en premier, triés par ordre croissant.
  2. Les enfants avec une valeur de chaîne comme clé viennent ensuite, triés lexicographiquement par ordre croissant.

orderByValue

Lors de l'utilisation orderByValue() , les enfants sont triés par leur valeur. Les critères de classement sont les mêmes que dans orderByChild() , sauf que la valeur du nœud est utilisée à la place de la valeur d'une clé enfant spécifiée.