เอกสารนี้ครอบคลุมการทำงานกับรายการข้อมูลใน Firebase ดูข้อมูลพื้นฐานเกี่ยวกับการอ่านและเขียนข้อมูล Firebase ได้ที่อ่านและเขียนข้อมูลใน Android
รับ DatabaseReference
หากต้องการอ่านและเขียนข้อมูลจากฐานข้อมูล คุณต้องมีอินสแตนซ์ของ DatabaseReference ดังนี้
Kotlin
private lateinit var database: DatabaseReference // ... database = Firebase.database.reference
Java
private DatabaseReference mDatabase; // ... mDatabase = FirebaseDatabase.getInstance().getReference();
อ่านและเขียนรายการ
ต่อท้ายรายการข้อมูล
ใช้เมธอด push() เพื่อเพิ่มข้อมูลต่อท้ายรายการในแอปพลิเคชันที่มีผู้ใช้หลายคน
เมธอด push() จะสร้างคีย์ที่ไม่ซ้ำกันทุกครั้งที่มีการเพิ่มรายการย่อยใหม่ลงในข้อมูลอ้างอิง Firebase ที่ระบุ การใช้คีย์ที่สร้างขึ้นโดยอัตโนมัติเหล่านี้สำหรับองค์ประกอบใหม่แต่ละรายการในรายการช่วยให้ไคลเอ็นต์หลายรายสามารถเพิ่มองค์ประกอบย่อยไปยังตำแหน่งเดียวกันพร้อมกันได้โดยไม่มีข้อขัดแย้งในการเขียน คีย์ที่ไม่ซ้ำกันซึ่งสร้างโดย push() จะอิงตามการประทับเวลา ดังนั้นระบบจะจัดเรียงรายการในลิสต์ตามลำดับเวลาโดยอัตโนมัติ
คุณสามารถใช้การอ้างอิงถึงข้อมูลใหม่ที่แสดงผลโดยเมธอด push() เพื่อรับค่าของคีย์ที่สร้างขึ้นโดยอัตโนมัติของรายการย่อยหรือตั้งค่าข้อมูลสำหรับรายการย่อยได้ การเรียก getKey() ในการอ้างอิง push() จะแสดงผลค่าของคีย์ที่สร้างขึ้นโดยอัตโนมัติ
คุณสามารถใช้คีย์ที่สร้างขึ้นโดยอัตโนมัติเหล่านี้เพื่อลดความซับซ้อนของโครงสร้างข้อมูลได้ ดูข้อมูลเพิ่มเติมได้ที่ตัวอย่างการกระจายข้อมูล
รอรับเหตุการณ์ย่อย
เมื่อทำงานกับรายการ แอปพลิเคชันของคุณควรฟังเหตุการณ์ย่อยแทนเหตุการณ์ค่าที่ใช้สำหรับออบเจ็กต์เดียว
ระบบจะทริกเกอร์เหตุการณ์ย่อยเพื่อตอบสนองต่อการดำเนินการที่เฉพาะเจาะจงซึ่งเกิดขึ้นกับโหนดย่อยจากการดำเนินการ เช่น มีการเพิ่มโหนดย่อยใหม่ผ่านเมธอด push() หรือมีการอัปเดตโหนดย่อยผ่านเมธอด updateChildren()
เมื่อใช้ร่วมกัน แต่ละรายการจะมีประโยชน์ในการฟังการเปลี่ยนแปลงของโหนดที่เฉพาะเจาะจงในฐานข้อมูล
หากต้องการฟังเหตุการณ์ย่อยใน DatabaseReference ให้แนบ ChildEventListener ดังนี้
| การส่งแบบฟอร์ม | Callback ของเหตุการณ์ | การใช้งานทั่วไป |
|---|---|---|
ChildEventListener
| onChildAdded() |
ดึงข้อมูลรายการสิ่งของหรือฟังการเพิ่มสิ่งของลงในรายการ
ระบบจะเรียกใช้ Callback นี้ 1 ครั้งสำหรับรายการย่อยที่มีอยู่แต่ละรายการ จากนั้นจะเรียกใช้อีกครั้งทุกครั้งที่มีการเพิ่มรายการย่อยใหม่ลงในเส้นทางที่ระบุ DataSnapshot ที่ส่งไปยัง Listener จะมีข้อมูลของรายการย่อยใหม่
|
onChildChanged() |
ฟังการเปลี่ยนแปลงของรายการในลิสต์ เหตุการณ์นี้จะเริ่มทำงานทุกครั้งที่มีการแก้ไขโหนดย่อย รวมถึงการแก้ไขใดๆ กับโหนดสืบทอดของโหนดย่อย DataSnapshot ที่ส่งไปยัง Listener เหตุการณ์มีข้อมูลที่อัปเดตสำหรับบุตรหลาน
|
|
onChildRemoved() |
ฟังรายการที่ถูกนำออกจากลิสต์ DataSnapshot ที่ส่งไปยัง Callback ของเหตุการณ์จะมีข้อมูลของบุตรหลานที่ถูกนำออก
|
|
onChildMoved() |
ฟังการเปลี่ยนแปลงลำดับของรายการในรายการตามลำดับ
ระบบจะทริกเกอร์เหตุการณ์นี้เมื่อใดก็ตามที่onChildChanged()
Callback ถูกทริกเกอร์โดยการอัปเดตที่ทำให้เกิดการจัดเรียงรายการย่อยใหม่
โดยจะใช้กับข้อมูลที่จัดเรียงด้วย orderByChild หรือ orderByValue
|
ตัวอย่างเช่น แอปบล็อกโซเชียลอาจใช้วิธีการเหล่านี้ร่วมกันเพื่อตรวจสอบกิจกรรมในความคิดเห็นของโพสต์ ดังที่แสดงด้านล่าง
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);
ฟังเหตุการณ์ค่า
แม้ว่าการใช้ ChildEventListener จะเป็นวิธีที่แนะนำในการอ่านรายการข้อมูล แต่ก็มีบางสถานการณ์ที่การแนบ ValueEventListener กับการอ้างอิงรายการจะมีประโยชน์
การแนบ ValueEventListener กับรายการข้อมูลจะแสดงผลรายการข้อมูลทั้งหมดเป็น DataSnapshot เดียว ซึ่งคุณสามารถวนซ้ำเพื่อเข้าถึงรายการย่อยแต่ละรายการได้
แม้ว่าจะมีรายการที่ตรงกันเพียงรายการเดียวสำหรับคำค้นหา แต่ภาพรวมก็ยังคงเป็นรายการที่มีเพียงรายการเดียว หากต้องการเข้าถึงรายการ คุณต้องวนซ้ำผลลัพธ์ดังนี้
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()); // ... } });
รูปแบบนี้จะมีประโยชน์เมื่อคุณต้องการดึงข้อมูลรายการย่อยทั้งหมดของรายการในการดำเนินการเดียว แทนที่จะรอฟังเหตุการณ์ onChildAdded เพิ่มเติม
แยก Listener
ระบบจะนำ Callback ออกโดยเรียกใช้เมธอด removeEventListener() ในการอ้างอิงฐานข้อมูล Firebase
หากมีการเพิ่ม Listener หลายครั้งในตำแหน่งข้อมูล ระบบจะเรียกใช้ Listener หลายครั้งสำหรับแต่ละเหตุการณ์ และคุณต้องแยก Listener ออกเป็นจำนวนครั้งเท่ากันเพื่อนำออกอย่างสมบูรณ์
การเรียกใช้ removeEventListener() ใน Listener ระดับบนสุดจะไม่นำ Listener ที่ลงทะเบียนไว้ในโหนดระดับล่างออกโดยอัตโนมัติ คุณต้องเรียกใช้ removeEventListener() ใน Listener ระดับล่างด้วยเพื่อนำ Callback ออก
การจัดเรียงและการกรองข้อมูล
คุณสามารถใช้คลาส Realtime Database Query เพื่อดึงข้อมูลที่จัดเรียงตามคีย์ ตามค่า หรือตามค่าของรายการย่อยได้ นอกจากนี้ คุณยังกรองผลลัพธ์ที่จัดเรียงแล้วให้แสดงผลลัพธ์ตามจำนวนที่ต้องการ หรือแสดงคีย์หรือค่าตามช่วงที่ต้องการได้ด้วย
จัดเรียงข้อมูล
หากต้องการดึงข้อมูลที่จัดเรียงแล้ว ให้เริ่มด้วยการระบุเมธอดการจัดเรียงลำดับวิธีใดวิธีหนึ่งเพื่อกำหนดวิธีจัดเรียงผลลัพธ์ ดังนี้
| วิธีการ | การใช้งาน |
|---|---|
orderByChild() |
จัดเรียงผลลัพธ์ตามค่าของคีย์ย่อยที่ระบุหรือเส้นทางย่อยที่ซ้อนกัน |
orderByKey()
| จัดเรียงผลลัพธ์ตามคีย์ย่อย |
orderByValue() |
จัดเรียงผลลัพธ์ตามค่าของรายการย่อย |
คุณใช้เมธอดการจัดเรียงลำดับได้ครั้งละ 1 เมธอดเท่านั้น การเรียกใช้เมธอดการจัดเรียงลำดับหลายครั้งในการค้นหาเดียวกันจะทำให้เกิดข้อผิดพลาด
ตัวอย่างต่อไปนี้แสดงวิธีดึงข้อมูลรายการโพสต์ยอดนิยมของผู้ใช้ที่จัดเรียงตามจำนวนดาว
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 // ... });
ซึ่งจะกำหนดการค้นหาที่เมื่อรวมกับ child listener จะซิงค์ไคลเอ็นต์กับโพสต์ของผู้ใช้จากเส้นทางในฐานข้อมูลตามรหัสผู้ใช้ โดยเรียงลำดับตามจำนวนดาวที่แต่ละโพสต์ได้รับ เทคนิคการใช้รหัสเป็นคีย์ดัชนีนี้เรียกว่าการกระจายข้อมูล คุณสามารถอ่านเพิ่มเติมได้ในจัดโครงสร้างฐานข้อมูล
การเรียกใช้เมธอด orderByChild() จะระบุคีย์ย่อยเพื่อจัดเรียงผลลัพธ์ตามคีย์นั้น ในกรณีนี้ ระบบจะจัดเรียงโพสต์ตามค่าของ "starCount" ย่อยที่เกี่ยวข้อง นอกจากนี้ยังสามารถจัดเรียงการค้นหาตามรายการย่อยที่ซ้อนกันได้ ในกรณีที่คุณมีข้อมูลที่มีลักษณะดังนี้
"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",
}
},ในตัวอย่างนี้ เราสามารถจัดเรียงองค์ประกอบรายการตามค่าที่ฝังอยู่ใต้คีย์ metrics โดยระบุ Relative Path ไปยังรายการย่อยที่ฝังอยู่ในการเรียก 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 // ... });
ดูข้อมูลเพิ่มเติมเกี่ยวกับวิธีจัดเรียงข้อมูลประเภทอื่นๆ ได้ที่วิธีจัดเรียงข้อมูลการค้นหา
การกรองข้อมูล
หากต้องการกรองข้อมูล คุณสามารถรวมเมธอดการจำกัดหรือเมธอดช่วงเข้ากับเมธอดการจัดเรียงลำดับเมื่อสร้างการค้นหา
| วิธีการ | การใช้งาน |
|---|---|
limitToFirst() |
กำหนดจำนวนรายการสูงสุดที่จะแสดงผลจากจุดเริ่มต้นของรายการผลลัพธ์ตามลำดับ |
limitToLast() |
กำหนดจำนวนรายการสูงสุดที่จะแสดงผลจากส่วนท้ายของรายการผลลัพธ์ที่เรียงลำดับแล้ว |
startAt() |
แสดงผลรายการที่มากกว่าหรือเท่ากับคีย์หรือค่าที่ระบุ โดยขึ้นอยู่กับเมธอดการจัดเรียงลำดับที่เลือก |
startAfter() |
แสดงผลรายการที่มากกว่าคีย์หรือค่าที่ระบุ โดยขึ้นอยู่กับเมธอดการจัดเรียงลำดับที่เลือก |
endAt() |
แสดงผลรายการที่น้อยกว่าหรือเท่ากับคีย์หรือค่าที่ระบุ โดยขึ้นอยู่กับเมธอดการจัดเรียงลำดับที่เลือก |
endBefore() |
แสดงผลรายการที่น้อยกว่าคีย์หรือค่าที่ระบุ โดยขึ้นอยู่กับเมธอดการจัดเรียงลำดับที่เลือก |
equalTo() |
แสดงผลรายการที่เท่ากับคีย์หรือค่าที่ระบุ โดยขึ้นอยู่กับเมธอดการจัดเรียงลำดับที่เลือก |
คุณสามารถรวมฟังก์ชันขีดจำกัดหรือช่วงหลายรายการเข้าด้วยกันได้ ซึ่งแตกต่างจากเมธอดการจัดเรียงลำดับ
เช่น คุณสามารถรวมเมธอด startAt() และ endAt() เพื่อจำกัดผลลัพธ์ให้อยู่ในช่วงค่าที่ระบุได้
แม้ว่าจะมีรายการที่ตรงกันเพียงรายการเดียวสำหรับคำค้นหา แต่ภาพรวมก็ยังคงเป็นรายการที่มีเพียงรายการเดียว หากต้องการเข้าถึงรายการ คุณต้องวนซ้ำผลลัพธ์ดังนี้
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()); // ... } });
จำกัดจำนวนผลลัพธ์
คุณสามารถใช้เมธอด limitToFirst() และ limitToLast() เพื่อกำหนดจำนวนสูงสุดของรายการย่อยที่จะซิงค์สำหรับการเรียกกลับที่ระบุ เช่น หากคุณใช้ limitToFirst() เพื่อตั้งขีดจำกัดไว้ที่ 100 ในตอนแรกคุณจะได้รับ Callback ของ onChildAdded() สูงสุดเพียง 100 รายการเท่านั้น หากคุณมีรายการที่จัดเก็บไว้ในฐานข้อมูล Firebase น้อยกว่า 100 รายการ ระบบจะเรียกใช้ Callback onChildAdded() สำหรับแต่ละรายการ
เมื่อรายการมีการเปลี่ยนแปลง คุณจะได้รับ Callback onChildAdded() รายการสำหรับรายการที่เข้าสู่การค้นหา และ Callback onChildRemoved() รายการสำหรับรายการที่ออกจากการค้นหา เพื่อให้จำนวนรวมยังคงอยู่ที่ 100 รายการ
ตัวอย่างต่อไปนี้แสดงวิธีที่แอปบล็อกตัวอย่างกำหนดการค้นหาเพื่อดึงข้อมูลรายการโพสต์ล่าสุด 100 รายการของผู้ใช้ทั้งหมด
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);
ตัวอย่างนี้กำหนดเฉพาะการค้นหาเท่านั้น หากต้องการซิงค์ข้อมูลจริง คุณต้องมี Listener ที่แนบมาด้วย
กรองตามคีย์หรือค่า
คุณสามารถใช้ startAt(), startAfter(), endAt(), endBefore() และ equalTo() เพื่อเลือกจุดเริ่มต้น จุดสิ้นสุด และจุดที่เทียบเท่ากันสำหรับคำค้นหาได้ ซึ่งอาจเป็นประโยชน์สำหรับการแบ่งหน้าข้อมูลหรือค้นหารายการที่มีรายการย่อยซึ่งมีค่าที่เฉพาะเจาะจง
วิธีจัดเรียงข้อมูลการค้นหา
ส่วนนี้จะอธิบายวิธีจัดเรียงข้อมูลตามเมธอดการจัดเรียงลำดับแต่ละเมธอดในคลาส Query
orderByChild
เมื่อใช้ orderByChild() ข้อมูลที่มีคีย์ย่อยที่ระบุจะเรียงลำดับดังนี้
- รายการย่อยที่มีค่า
nullสำหรับคีย์ย่อยที่ระบุจะแสดงก่อน - ตามด้วยรายการย่อยที่มีค่า
falseสำหรับคีย์ย่อยที่ระบุ หากโหนดบุตรหลายโหนดมีค่าเป็นfalseระบบจะจัดเรียงโหนดบุตรเหล่านั้นตามพจนานุกรมตามคีย์ - ตามด้วยรายการย่อยที่มีค่า
trueสำหรับคีย์ย่อยที่ระบุ หากองค์ประกอบย่อยหลายรายการมีค่าเป็นtrueระบบจะจัดเรียงตามพจนานุกรมตามคีย์ - ตามด้วยรายการย่อยที่มีค่าตัวเลข โดยจัดเรียงจากน้อยไปมาก หากโหนดย่อยหลายโหนดมีค่าตัวเลขเดียวกันสำหรับโหนดย่อยที่ระบุ ระบบจะจัดเรียงตามคีย์
- สตริงจะอยู่หลังตัวเลขและจัดเรียงตามพจนานุกรมจากน้อยไปมาก หากโหนดย่อยหลายโหนดมีค่าเดียวกันสำหรับโหนดย่อยที่ระบุ ระบบจะจัดเรียงตามพจนานุกรมตามคีย์
- ออบเจ็กต์จะอยู่ท้ายสุดและจัดเรียงตามพจนานุกรมตามคีย์จากน้อยไปมาก
orderByKey
เมื่อใช้ orderByKey() เพื่อจัดเรียงข้อมูล ระบบจะแสดงผลข้อมูลตามลำดับจากน้อยไปมากตามคีย์
- รายการย่อยที่มีคีย์ที่แยกวิเคราะห์เป็นจำนวนเต็ม 32 บิตได้จะแสดงก่อน โดยจัดเรียงตามลำดับจากน้อยไปมาก
- รายการย่อยที่มีค่าสตริงเป็นคีย์จะแสดงถัดไป โดยจัดเรียงตามพจนานุกรมจากน้อยไปมาก
orderByValue
เมื่อใช้ orderByValue() ระบบจะจัดเรียงองค์ประกอบย่อยตามค่าขององค์ประกอบ เกณฑ์การจัดลำดับจะเหมือนกับใน orderByChild() ยกเว้นว่าจะใช้ค่าของโหนดแทนค่าของคีย์ย่อยที่ระบุ