Ce document explique comment travailler avec des listes de données dans Firebase. Pour découvrir les bases de la lecture et de l'écriture de données Firebase, consultez Lire et écrire des données sur Android.
Obtenir un DatabaseReference
Pour lire et écrire des données dans la base de données, vous avez besoin d'une instance de DatabaseReference
:
Kotlin
private lateinit var database: DatabaseReference // ... database = Firebase.database.reference
Java
private DatabaseReference mDatabase; // ... mDatabase = FirebaseDatabase.getInstance().getReference();
Lire et écrire des listes
Ajouter des données à une liste
Utilisez la méthode push()
pour ajouter des données à une liste dans les applications multi-utilisateurs.
La méthode push()
génère une clé unique chaque fois qu'un enfant est ajouté à la référence Firebase spécifiée. En utilisant ces clés générées automatiquement pour chaque nouvel élément de la liste, plusieurs clients peuvent ajouter des enfants au même emplacement en même temps sans conflits d'écriture. La clé unique générée par push()
est basée sur un code temporel. Les éléments de la liste sont donc automatiquement classés par ordre chronologique.
Vous pouvez utiliser la référence aux nouvelles données renvoyées par la méthode push()
pour obtenir la valeur de la clé générée automatiquement de l'enfant ou définir des données pour l'enfant. L'appel de getKey()
sur une référence push()
renvoie la valeur de la clé générée automatiquement.
Vous pouvez utiliser ces clés générées automatiquement pour simplifier l'aplatissement de votre structure de données. Pour en savoir plus, consultez l'exemple de répartition des données.
Écouter les événements enfants
Lorsque vous travaillez avec des listes, votre application doit écouter les événements enfants plutôt que les événements de valeur utilisés pour les objets uniques.
Les événements enfants sont déclenchés en réponse à des opérations spécifiques qui se produisent sur les enfants d'un nœud à partir d'une opération telle qu'un nouvel enfant ajouté via la méthode push()
ou un enfant mis à jour via la méthode updateChildren()
.
Ces éléments peuvent être utiles pour écouter les modifications apportées à un nœud spécifique d'une base de données.
Pour écouter les événements enfants sur DatabaseReference
, associez un ChildEventListener
:
Écouteur | Rappel d'événement | Utilisation typique |
---|---|---|
ChildEventListener
| onChildAdded() |
Récupérer des listes d'éléments ou écouter les éléments ajoutés à une liste
Ce rappel est déclenché une fois pour chaque enfant existant, puis à chaque fois qu'un nouvel enfant est ajouté au chemin spécifié. Le DataSnapshot transmis au listener contient les données du nouvel enfant.
|
onChildChanged() |
Écoutez les modifications apportées aux éléments d'une liste. Cet événement se déclenche chaque fois qu'un nœud enfant est modifié, y compris les modifications apportées aux descendants du nœud enfant. Le DataSnapshot transmis à l'écouteur d'événements contient les données mises à jour pour l'enfant.
|
|
onChildRemoved() |
Écoutez les éléments supprimés d'une liste. Le DataSnapshot transmis au rappel d'événement contient les données de l'enfant supprimé.
|
|
onChildMoved() |
Écoutez les modifications apportées à l'ordre des éléments d'une liste ordonnée.
Cet événement est déclenché chaque fois que le rappel onChildChanged() est déclenché par une mise à jour qui entraîne le réordonnancement de l'enfant.
Il est utilisé avec des données triées avec orderByChild ou orderByValue .
|
Par exemple, une application de blog social peut utiliser ces méthodes ensemble pour surveiller l'activité dans les commentaires d'un post, comme indiqué ci-dessous :
Kotlin
val childEventListener = object : ChildEventListener { override fun onChildAdded(dataSnapshot: DataSnapshot, previousChildName: String?) { Log.d(TAG, "onChildAdded:" + dataSnapshot.key!!) // A new comment has been added, add it to the displayed list val comment = dataSnapshot.getValue<Comment>() // ... } override fun onChildChanged(dataSnapshot: DataSnapshot, previousChildName: String?) { Log.d(TAG, "onChildChanged: ${dataSnapshot.key}") // A comment has changed, use the key to determine if we are displaying this // comment and if so displayed the changed comment. val newComment = dataSnapshot.getValue<Comment>() val commentKey = dataSnapshot.key // ... } override fun onChildRemoved(dataSnapshot: DataSnapshot) { Log.d(TAG, "onChildRemoved:" + dataSnapshot.key!!) // A comment has changed, use the key to determine if we are displaying this // comment and if so remove it. val commentKey = dataSnapshot.key // ... } override fun onChildMoved(dataSnapshot: DataSnapshot, previousChildName: String?) { Log.d(TAG, "onChildMoved:" + dataSnapshot.key!!) // A comment has changed position, use the key to determine if we are // displaying this comment and if so move it. val movedComment = dataSnapshot.getValue<Comment>() val commentKey = dataSnapshot.key // ... } override fun onCancelled(databaseError: DatabaseError) { Log.w(TAG, "postComments:onCancelled", databaseError.toException()) Toast.makeText( context, "Failed to load comments.", Toast.LENGTH_SHORT, ).show() } } databaseReference.addChildEventListener(childEventListener)
Java
ChildEventListener childEventListener = new ChildEventListener() { @Override public void onChildAdded(DataSnapshot dataSnapshot, String previousChildName) { Log.d(TAG, "onChildAdded:" + dataSnapshot.getKey()); // A new comment has been added, add it to the displayed list Comment comment = dataSnapshot.getValue(Comment.class); // ... } @Override public void onChildChanged(DataSnapshot dataSnapshot, String previousChildName) { Log.d(TAG, "onChildChanged:" + dataSnapshot.getKey()); // A comment has changed, use the key to determine if we are displaying this // comment and if so displayed the changed comment. Comment newComment = dataSnapshot.getValue(Comment.class); String commentKey = dataSnapshot.getKey(); // ... } @Override public void onChildRemoved(DataSnapshot dataSnapshot) { Log.d(TAG, "onChildRemoved:" + dataSnapshot.getKey()); // A comment has changed, use the key to determine if we are displaying this // comment and if so remove it. String commentKey = dataSnapshot.getKey(); // ... } @Override public void onChildMoved(DataSnapshot dataSnapshot, String previousChildName) { Log.d(TAG, "onChildMoved:" + dataSnapshot.getKey()); // A comment has changed position, use the key to determine if we are // displaying this comment and if so move it. Comment movedComment = dataSnapshot.getValue(Comment.class); String commentKey = dataSnapshot.getKey(); // ... } @Override public void onCancelled(DatabaseError databaseError) { Log.w(TAG, "postComments:onCancelled", databaseError.toException()); Toast.makeText(mContext, "Failed to load comments.", Toast.LENGTH_SHORT).show(); } }; databaseReference.addChildEventListener(childEventListener);
Écouter les événements de valeur
Bien que l'utilisation d'un ChildEventListener
soit la méthode recommandée pour lire des listes de données, il existe des situations où il est utile d'associer un ValueEventListener
à une référence de liste.
Si vous associez un ValueEventListener
à une liste de données, la liste entière sera renvoyée sous forme d'un seul DataSnapshot
, sur lequel vous pourrez ensuite effectuer une boucle pour accéder à chaque enfant.
Même lorsqu'il n'y a qu'une seule correspondance pour la requête, l'instantané reste une liste, mais ne contient qu'un seul élément. Pour accéder à l'élément, vous devez parcourir le résultat :
Kotlin
// My top posts by number of stars myTopPostsQuery.addValueEventListener(object : ValueEventListener { override fun onDataChange(dataSnapshot: DataSnapshot) { for (postSnapshot in dataSnapshot.children) { // TODO: handle the post } } override fun onCancelled(databaseError: DatabaseError) { // Getting Post failed, log a message Log.w(TAG, "loadPost:onCancelled", databaseError.toException()) // ... } })
Java
// My top posts by number of stars myTopPostsQuery.addValueEventListener(new ValueEventListener() { @Override public void onDataChange(@NonNull DataSnapshot dataSnapshot) { for (DataSnapshot postSnapshot: dataSnapshot.getChildren()) { // TODO: handle the post } } @Override public void onCancelled(@NonNull DatabaseError databaseError) { // Getting Post failed, log a message Log.w(TAG, "loadPost:onCancelled", databaseError.toException()); // ... } });
Ce modèle peut être utile lorsque vous souhaitez récupérer tous les enfants d'une liste en une seule opération, plutôt que d'écouter d'autres événements onChildAdded
.
Détacher les écouteurs
Les fonctions de rappel sont supprimées en appelant la méthode removeEventListener()
sur votre référence de base de données Firebase.
Si un écouteur a été ajouté plusieurs fois à un emplacement de données, il est appelé plusieurs fois pour chaque événement. Vous devez le détacher le même nombre de fois pour le supprimer complètement.
L'appel de removeEventListener()
sur un écouteur parent ne supprime pas automatiquement les écouteurs enregistrés sur ses nœuds enfants. removeEventListener()
doit également être appelé sur tous les écouteurs enfants pour supprimer le rappel.
Trier et filtrer les données
Vous pouvez utiliser la classe Realtime Database Query
pour récupérer des données triées par clé, par valeur ou par valeur d'un enfant. Vous pouvez également filtrer le résultat trié sur un nombre spécifique de résultats ou sur une plage de clés ou de valeurs.
Trier les données
Pour récupérer des données triées, commencez par spécifier l'une des méthodes order-by pour déterminer l'ordre des résultats :
Méthode | Utilisation |
---|---|
orderByChild() |
Triez les résultats par la valeur d'une clé enfant ou d'un chemin enfant imbriqué spécifiés. |
orderByKey()
| Triez les résultats par clés enfants. |
orderByValue() |
Triez les résultats par valeurs enfants. |
Vous ne pouvez utiliser qu'une seule méthode order-by à la fois. L'appel d'une méthode order-by plusieurs fois dans la même requête génère une erreur.
L'exemple suivant montre comment récupérer la liste des posts les plus populaires d'un utilisateur, triés par nombre d'étoiles :
Kotlin
// My top posts by number of stars val myUserId = uid val myTopPostsQuery = databaseReference.child("user-posts").child(myUserId) .orderByChild("starCount") myTopPostsQuery.addChildEventListener(object : ChildEventListener { // TODO: implement the ChildEventListener methods as documented above // ... })
Java
// My top posts by number of stars String myUserId = getUid(); Query myTopPostsQuery = databaseReference.child("user-posts").child(myUserId) .orderByChild("starCount"); myTopPostsQuery.addChildEventListener(new ChildEventListener() { // TODO: implement the ChildEventListener methods as documented above // ... });
Cela définit une requête qui, combinée à un écouteur enfant, synchronise le client avec les posts de l'utilisateur à partir du chemin d'accès dans la base de données en fonction de son ID utilisateur, classés par le nombre d'étoiles que chaque post a reçues. Cette technique d'utilisation des ID comme clés d'index est appelée "expansion des données". Pour en savoir plus, consultez Structurer votre base de données.
L'appel à la méthode orderByChild()
spécifie la clé enfant selon laquelle trier les résultats. Dans ce cas, les posts sont triés par la valeur de leur enfant "starCount"
respectif. Les requêtes peuvent également être triées par enfants imbriqués, si vous avez des données qui ressemblent à ceci :
"posts": { "ts-functions": { "metrics": { "views" : 1200000, "likes" : 251000, "shares": 1200, }, "title" : "Why you should use TypeScript for writing Cloud Functions", "author": "Doug", }, "android-arch-3": { "metrics": { "views" : 900000, "likes" : 117000, "shares": 144, }, "title" : "Using Android Architecture Components with Firebase Realtime Database (Part 3)", "author": "Doug", } },
Dans cet exemple, nous pouvons trier les éléments de notre liste par valeurs imbriquées sous la clé metrics
en spécifiant le chemin d'accès relatif à l'enfant imbriqué dans notre appel orderByChild()
.
Kotlin
// Most viewed posts val myMostViewedPostsQuery = databaseReference.child("posts") .orderByChild("metrics/views") myMostViewedPostsQuery.addChildEventListener(object : ChildEventListener { // TODO: implement the ChildEventListener methods as documented above // ... })
Java
// Most viewed posts Query myMostViewedPostsQuery = databaseReference.child("posts") .orderByChild("metrics/views"); myMostViewedPostsQuery.addChildEventListener(new ChildEventListener() { // TODO: implement the ChildEventListener methods as documented above // ... });
Pour en savoir plus sur l'ordre des autres types de données, consultez Ordre des données de requête.
Filtrer les données
Pour filtrer les données, vous pouvez combiner l'une des méthodes de limite ou de plage avec une méthode d'ordre lorsque vous créez une requête.
Méthode | Utilisation |
---|---|
limitToFirst() |
Définit le nombre maximal d'éléments à renvoyer depuis le début de la liste ordonnée des résultats. |
limitToLast() |
Définit le nombre maximal d'éléments à renvoyer à partir de la fin de la liste ordonnée des résultats. |
startAt() |
Renvoie les éléments dont la clé ou la valeur est supérieure ou égale à celle spécifiée, selon la méthode de tri choisie. |
startAfter() |
Renvoie les éléments supérieurs à la clé ou à la valeur spécifiée, selon la méthode de tri choisie. |
endAt() |
Renvoie les éléments dont la clé ou la valeur sont inférieures ou égales à celles spécifiées, selon la méthode de tri choisie. |
endBefore() |
Renvoie les éléments inférieurs à la clé ou à la valeur spécifiée, selon la méthode de tri choisie. |
equalTo() |
Renvoie les éléments égaux à la clé ou à la valeur spécifiées, en fonction de la méthode de tri choisie. |
Contrairement aux méthodes order-by, vous pouvez combiner plusieurs fonctions limit ou range.
Par exemple, vous pouvez combiner les méthodes startAt()
et endAt()
pour limiter les résultats à une plage de valeurs spécifiée.
Même s'il n'y a qu'un seul résultat pour la requête, l'instantané reste une liste, mais ne contient qu'un seul élément. Pour accéder à l'élément, vous devez parcourir le résultat en boucle :
Kotlin
// My top posts by number of stars myTopPostsQuery.addValueEventListener(object : ValueEventListener { override fun onDataChange(dataSnapshot: DataSnapshot) { for (postSnapshot in dataSnapshot.children) { // TODO: handle the post } } override fun onCancelled(databaseError: DatabaseError) { // Getting Post failed, log a message Log.w(TAG, "loadPost:onCancelled", databaseError.toException()) // ... } })
Java
// My top posts by number of stars myTopPostsQuery.addValueEventListener(new ValueEventListener() { @Override public void onDataChange(@NonNull DataSnapshot dataSnapshot) { for (DataSnapshot postSnapshot: dataSnapshot.getChildren()) { // TODO: handle the post } } @Override public void onCancelled(@NonNull DatabaseError databaseError) { // Getting Post failed, log a message Log.w(TAG, "loadPost:onCancelled", databaseError.toException()); // ... } });
Limiter le nombre de résultats
Vous pouvez utiliser les méthodes limitToFirst()
et limitToLast()
pour définir un nombre maximal d'enfants à synchroniser pour un rappel donné. Par exemple, si vous utilisez limitToFirst()
pour définir une limite de 100, vous ne recevez initialement que 100 rappels onChildAdded()
au maximum. Si votre base de données Firebase contient moins de 100 éléments, un rappel onChildAdded()
est déclenché pour chaque élément.
À mesure que les éléments changent, vous recevez des rappels onChildAdded()
pour les éléments qui entrent dans la requête et des rappels onChildRemoved()
pour les éléments qui en sortent, de sorte que le nombre total reste à 100.
L'exemple suivant montre comment une application de blog type définit une requête pour récupérer la liste des 100 derniers posts de tous les utilisateurs :
Kotlin
// Last 100 posts, these are automatically the 100 most recent // due to sorting by push() keys. databaseReference.child("posts").limitToFirst(100)
Java
// Last 100 posts, these are automatically the 100 most recent // due to sorting by push() keys Query recentPostsQuery = databaseReference.child("posts") .limitToFirst(100);
Cet exemple ne définit qu'une requête. Pour synchroniser réellement les données, il doit être associé à un écouteur.
Filtrer par clé ou par valeur
Vous pouvez utiliser startAt()
, startAfter()
, endAt()
, endBefore()
et equalTo()
pour choisir des points de départ, de fin et d'équivalence arbitraires pour les requêtes. Cela peut être utile pour paginer des données ou trouver des éléments enfants ayant une valeur spécifique.
Ordre des données de requête
Cette section explique comment les données sont triées par chacune des méthodes order-by de la classe Query
.
orderByChild
Lorsque vous utilisez orderByChild()
, les données contenant la clé enfant spécifiée sont ordonnées comme suit :
- Les enfants dont la valeur
null
correspond à la clé enfant spécifiée sont affichés en premier. - Viennent ensuite les enfants dont la valeur est
false
pour la clé enfant spécifiée. Si plusieurs enfants ont une valeur defalse
, ils sont triés lexicographiquement par clé. - Viennent ensuite les enfants dont la valeur est
true
pour la clé enfant spécifiée. Si plusieurs enfants ont une valeur detrue
, ils sont triés de façon lexicographique par clé. - Les enfants avec une valeur numérique suivent, 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é.
- Les chaînes viennent après les nombres et sont triées par ordre lexicographique croissant. Si plusieurs enfants ont la même valeur pour le nœud enfant spécifié, ils sont triés de façon lexicographique par clé.
- Les objets sont placés en dernier et triés de manière lexicographique par clé, dans l'ordre croissant.
orderByKey
Lorsque vous utilisez orderByKey()
pour trier vos données, celles-ci sont renvoyées par clé dans l'ordre croissant.
- Les enfants dont la clé peut être analysée comme un entier de 32 bits sont affichés en premier, par ordre croissant.
- Les enfants dont la clé est une valeur de chaîne viennent ensuite, triés par ordre lexicographique croissant.
orderByValue
Lorsque vous utilisez orderByValue()
, les enfants sont classés par ordre de valeur. Les critères de tri 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.