Dokumen ini menjelaskan cara menangani daftar data di Firebase. Untuk mempelajari dasar-dasar pembacaan dan penulisan data di Firebase, baca Membaca dan Menulis Data di Android.
Mendapatkan DatabaseReference
Untuk membaca dan menulis data dari database, Anda memerlukan instance DatabaseReference
:
Kotlin+KTX
private lateinit var database: DatabaseReference // ... database = Firebase.database.reference
Java
private DatabaseReference mDatabase; // ... mDatabase = FirebaseDatabase.getInstance().getReference();
Membaca dan menulis daftar
Menambahkan ke daftar data
Gunakan metode push()
untuk menambahkan data ke daftar dalam aplikasi multipengguna.
Metode push()
menghasilkan kunci unik setiap kali turunan baru ditambahkan ke referensi Firebase tertentu. Dengan kunci yang dihasilkan secara otomatis untuk setiap elemen baru dalam daftar, beberapa klien dapat menambahkan turunan ke lokasi yang sama secara bersamaan tanpa mengalami konflik penulisan. Kunci unik yang dihasilkan oleh push()
didasarkan pada stempel waktu, sehingga item daftar otomatis diurutkan secara kronologis.
Anda dapat menggunakan referensi ke data baru yang ditampilkan oleh metode push()
untuk mendapatkan nilai kunci turunan yang dihasilkan otomatis atau menetapkan data untuk turunan. Memanggil
getKey()
pada referensi push()
akan menampilkan nilai kunci yang dihasilkan secara otomatis.
Anda dapat menggunakan kunci yang dihasilkan secara otomatis ini untuk mempermudah perataan struktur data. Untuk mengetahui informasi lebih lanjut, lihat contoh fan-out data.
Memproses peristiwa turunan
Saat menangani daftar, aplikasi Anda harus memproses peristiwa turunan, bukan peristiwa nilai yang digunakan untuk objek tunggal.
Peristiwa turunan dipicu sebagai respons terhadap operasi tertentu yang terjadi pada turunan node dari suatu operasi, seperti turunan baru yang ditambahkan melalui metode push()
atau turunan yang diperbarui melalui metode updateChildren()
.
Secara bersama-sama, setiap metode ini berguna untuk memproses perubahan pada node tertentu dalam suatu database.
Untuk memproses peristiwa turunan pada DatabaseReference
, tambahkan ChildEventListener
:
Listener | Callback peristiwa | Penggunaan standar |
---|---|---|
ChildEventListener
| onChildAdded() |
Mengambil daftar item atau memproses penambahan ke daftar item.
Callback ini dipicu satu kali untuk setiap turunan yang ada, dan dipicu lagi setiap kali ada turunan baru yang ditambahkan ke jalur yang ditentukan. DataSnapshot yang diteruskan ke pemroses memuat data dari turunan baru.
|
onChildChanged() |
Memproses perubahan pada item dalam daftar. Peristiwa ini dipicu setiap kali node turunan diubah, termasuk setiap perubahan pada turunan dari node turunan. DataSnapshot yang diteruskan ke pemroses peristiwa berisi data turunan yang diperbarui.
|
|
onChildRemoved() |
Memproses item yang dihapus dari daftar. DataSnapshot yang diteruskan ke callback peristiwa berisi data untuk turunan yang dihapus.
|
|
onChildMoved() |
Memproses perubahan urutan item dalam daftar yang diurutkan.
Peristiwa ini dipicu setiap kali callback onChildChanged() dipicu oleh pembaruan yang menyebabkan pengurutan ulang turunan.
Peristiwa ini digunakan dengan data yang diurutkan dengan orderByChild atau orderByValue .
|
Misalnya, aplikasi blogging sosial mungkin menggunakan metode ini secara bersamaan untuk memantau aktivitas dalam komentar suatu postingan, seperti terlihat di bawah ini:
Kotlin+KTX
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);
Memproses peristiwa nilai
Meskipun penggunaan ChildEventListener
direkomendasikan untuk membaca daftar data, penambahan ValueEventListener
ke referensi daftar juga dapat berguna dalam situasi tertentu.
Penyertaan ValueEventListener
ke daftar data akan menampilkan keseluruhan daftar data sebagai DataSnapshot
tunggal, yang nantinya dapat di-loop untuk mengakses setiap turunan.
Meskipun jika hanya ada satu kueri yang cocok, snapshot tetap merupakan sebuah daftar yang, dalam hal ini, berisi hanya satu item. Untuk mengakses item tersebut, Anda harus melakukan loop hasilnya:
Kotlin+KTX
// 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()); // ... } });
Pola ini akan berguna saat Anda ingin mengambil semua turunan dari daftar dalam satu operasi, alih-alih memproses peristiwa onChildAdded
tambahan.
Melepas pemroses
Callback dihapus dengan memanggil metode removeEventListener()
pada referensi database Firebase.
Jika telah ditambahkan beberapa kali ke lokasi data, pemroses akan dipanggil beberapa kali untuk setiap peristiwa, dan Anda harus melepasnya dalam jumlah yang sama seperti saat menambahkannya agar terhapus semuanya.
Memanggil removeEventListener()
pada pemroses induk tidak akan menghapus pemroses yang terdaftar pada node turunannya secara otomatis. removeEventListener()
juga harus dipanggil pada pemroses turunan mana pun untuk menghapus callback.
Mengurutkan dan memfilter data
Anda dapat menggunakan class Realtime Database Query
untuk memperoleh data yang diurutkan berdasarkan kunci, nilai, atau nilai turunan. Anda juga dapat memfilter hasil pengurutan berdasarkan jumlah hasil tertentu atau berdasarkan rentang kunci atau nilai.
Mengurutkan data
Untuk mengambil data yang diurutkan, mulai dengan menentukan salah satu metode 'urutkan menurut' guna menentukan cara pengurutan hasil:
Metode | Penggunaan |
---|---|
orderByChild() |
Mengurutkan hasil menurut nilai kunci turunan atau jalur turunan bertingkat yang ditentukan. |
orderByKey()
| Mengurutkan hasil menurut kunci turunan. |
orderByValue() |
Mengurutkan hasil menurut nilai turunan. |
Anda hanya bisa menggunakan satu metode 'urutkan menurut' pada satu waktu. Memanggil metode 'urutkan menurut' beberapa kali dalam kueri yang sama akan menampilkan error.
Contoh berikut menunjukkan cara mengambil daftar postingan teratas milik pengguna yang diurutkan berdasarkan jumlah bintangnya:
Kotlin+KTX
// 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 // ... });
Contoh ini menentukan kueri yang, jika dikombinasikan dengan pemroses turunan, akan menyinkronkan klien dengan postingan pengguna dari jalur dalam database berdasarkan ID penggunanya, yang diurutkan menurut jumlah bintang yang diterima setiap postingan. Teknik penggunaan ID sebagai kunci indeks ini disebut fan-out data. Anda dapat membaca hal ini lebih lanjut di bagian Membuat Struktur Database.
Panggilan ke metode orderByChild()
menentukan kunci turunan yang dipakai sebagai dasar pengurutan hasil. Dalam kasus ini, postingan diurutkan menurut nilai turunan "starCount"
masing-masing. Kueri juga dapat diurutkan menurut turunan bertingkat, jika Anda memiliki data yang tampak seperti ini:
"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", } },
Dalam contoh ini, kita dapat mengurutkan elemen daftar menurut nilai bertingkat dalam kunci metrics
dengan menentukan jalur relatif ke turunan bertingkat pada panggilan orderByChild()
.
Kotlin+KTX
// 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 // ... });
Untuk mengetahui informasi lebih lanjut mengenai metode pengurutan jenis data lainnya, pelajari Cara data kueri diurutkan.
Memfilter data
Untuk memfilter data, Anda dapat menggabungkan salah satu metode batas atau rentang dengan metode 'urutkan menurut' ketika menyusun kueri.
Metode | Penggunaan |
---|---|
limitToFirst() |
Menetapkan jumlah item maksimum untuk ditampilkan dari awal daftar hasil yang diurutkan. |
limitToLast() |
Menetapkan jumlah item maksimum untuk ditampilkan dari akhir daftar hasil yang diurutkan. |
startAt() |
Menampilkan item yang lebih banyak atau sama dengan kunci atau nilai yang ditentukan, tergantung metode 'urutkan menurut' yang dipilih. |
startAfter() |
Menampilkan item yang lebih banyak dari kunci atau nilai yang ditentukan, tergantung metode 'urutkan menurut' yang dipilih. |
endAt() |
Menampilkan item yang lebih sedikit atau sama dengan kunci atau nilai yang ditentukan, tergantung metode 'urutkan menurut' yang dipilih. |
endBefore() |
Menampilkan item yang lebih sedikit dari kunci atau nilai yang ditentukan, tergantung metode 'urutkan menurut' yang dipilih. |
equalTo() |
Menampilkan item yang sama dengan kunci atau nilai yang ditentukan, tergantung metode 'urutkan menurut' yang dipilih. |
Berbeda dengan metode 'urutkan menurut', Anda dapat menggabungkan beberapa fungsi batas atau rentang.
Misalnya, Anda dapat menggabungkan metode startAt()
dan endAt()
untuk membatasi hasil ke rentang nilai tertentu.
Meskipun kueri hanya mendapatkan satu item yang cocok, snapshot-nya tetap berupa sebuah daftar, meski hanya memuat satu item. Untuk mengakses item tersebut, Anda harus melakukan loop atas hasil:
Kotlin+KTX
// 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()); // ... } });
Membatasi jumlah hasil
Anda dapat menggunakan metode limitToFirst()
dan limitToLast()
untuk menetapkan jumlah turunan maksimum yang akan disinkronkan untuk callback tertentu. Misalnya, jika Anda menggunakan limitToFirst()
untuk menetapkan batas 100, pada awalnya Anda hanya akan menerima hingga 100 callback onChildAdded()
. Jika Anda memiliki kurang dari 100 item yang disimpan dalam database Firebase, callback onChildAdded()
akan diaktifkan untuk setiap item.
Saat item berubah, Anda akan menerima callback onChildAdded()
untuk item yang masuk ke kueri dan callback onChildRemoved()
untuk item yang keluar dari kueri, sehingga jumlah totalnya tetap 100.
Contoh berikut menunjukkan cara aplikasi blogging menentukan kueri untuk mengambil daftar 100 postingan terbaru oleh semua pengguna:
Kotlin+KTX
// 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);
Contoh ini hanya menetapkan kueri. Untuk dapat benar-benar menyinkronkan data, perlu ada pemroses yang terpasang.
Memfilter berdasarkan kunci atau nilai
Anda dapat menggunakan startAt()
, startAfter()
, endAt()
, endBefore()
, dan equalTo()
untuk memilih titik awal, akhir, dan ekuivalensi yang sembarang untuk kueri. Hal ini dapat bermanfaat untuk memisahkan data ke dalam halaman atau menemukan item dengan turunan yang memiliki nilai tertentu.
Cara pengurutan data kueri
Bagian ini menjelaskan cara pengurutan data menggunakan setiap metode 'urutkan menurut' di class Query
.
orderByChild
Jika Anda menggunakan orderByChild()
, data yang berisi kunci turunan yang ditentukan akan diurutkan sebagai berikut:
- Turunan yang memiliki nilai
null
untuk kunci turunan yang ditentukan akan muncul terlebih dahulu. - Turunan yang memiliki nilai
false
untuk kunci turunan yang ditentukan akan muncul berikutnya. Jika beberapa turunan memiliki nilaifalse
, turunan tersebut akan diurutkan secara leksikografis menurut kunci. - Turunan yang memiliki nilai
true
untuk kunci turunan yang ditentukan akan muncul berikutnya. Jika beberapa turunan memiliki nilaitrue
, turunan tersebut akan diurutkan secara leksikografis menurut kunci. - Turunan dengan nilai numerik akan muncul berikutnya, dan diurutkan menaik. Jika beberapa turunan memiliki nilai numerik yang sama untuk node turunan yang ditentukan, turunan tersebut akan diurutkan menurut kunci.
- String muncul setelah angka, dan diurutkan secara leksikografis menaik. Jika beberapa turunan memiliki nilai yang sama untuk node turunan yang ditentukan, turunan tersebut akan diurutkan secara leksikografis menurut kunci.
- Objek akan muncul terakhir, dan diurutkan secara leksikografis menaik menurut kunci.
orderByKey
Data yang diurutkan menggunakan orderByKey()
akan ditampilkan dalam urutan menaik menurut kunci.
- Turunan dengan kunci yang bisa diurai sebagai bilangan bulat 32-bit akan muncul terlebih dahulu, dan diurutkan menaik.
- Turunan dengan nilai string sebagai kuncinya akan muncul berikutnya, dan diurutkan secara leksikografis menaik.
orderByValue
Jika menggunakan orderByValue()
, turunan akan diurutkan menurut nilainya. Kriteria urutan sama seperti di orderByChild()
, tetapi yang digunakan adalah nilai node, bukan nilai kunci turunan yang ditentukan.