เอกสารนี้ครอบคลุมการทำงานกับรายการข้อมูลใน 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()
จะแสดงผลค่าของคีย์ที่สร้างขึ้นโดยอัตโนมัติ
คุณสามารถใช้คีย์ที่สร้างขึ้นโดยอัตโนมัติเหล่านี้เพื่อลดความซับซ้อนในการแปลงโครงสร้างข้อมูล ให้เป็นรูปแบบแบน ดูข้อมูลเพิ่มเติมได้ที่ตัวอย่างการกระจายข้อมูล
ฟังเหตุการณ์ย่อย
เมื่อทำงานกับรายการ แอปพลิเคชันควรรับฟังเหตุการณ์ของออบเจ็กต์ย่อย แทนเหตุการณ์ค่าที่ใช้สำหรับออบเจ็กต์เดียว
เหตุการณ์ของ Child จะทริกเกอร์เพื่อตอบสนองต่อการดำเนินการที่เฉพาะเจาะจงซึ่งเกิดขึ้นกับ
Child ของโหนดจากการดำเนินการ เช่น การเพิ่ม Child ใหม่ผ่านเมธอด push()
หรือการอัปเดต Child ผ่านเมธอด updateChildren()
การใช้ฟีเจอร์เหล่านี้ร่วมกันจะมีประโยชน์ในการฟังการเปลี่ยนแปลงของโหนดที่เฉพาะเจาะจงในฐานข้อมูล
หากต้องการฟังเหตุการณ์ย่อยใน DatabaseReference
ให้แนบ
ChildEventListener
ดังนี้
การส่งแบบฟอร์ม | การเรียกกลับของเหตุการณ์ | การใช้งานทั่วไป |
---|---|---|
ChildEventListener
| onChildAdded() |
ดึงข้อมูลรายการหรือฟังการเพิ่มรายการลงในลิสต์
ระบบจะทริกเกอร์การเรียกกลับนี้ 1 ครั้งสำหรับบุตรหลานที่มีอยู่แต่ละคน จากนั้นจะทริกเกอร์อีกครั้ง
ทุกครั้งที่มีการเพิ่มบุตรหลานใหม่ลงในเส้นทางที่ระบุ DataSnapshot ที่ส่งไปยัง Listener จะมี
ข้อมูลของบุตรหลานคนใหม่
|
onChildChanged() |
ฟังการเปลี่ยนแปลงของรายการในลิสต์ เหตุการณ์นี้จะทริกเกอร์ทุกครั้งที่มีการแก้ไข
โหนดย่อย รวมถึงการแก้ไขใดๆ ที่มีต่อองค์ประกอบย่อยของ
โหนดย่อย DataSnapshot ที่ส่งไปยัง Listener เหตุการณ์มีข้อมูลที่อัปเดตแล้วสำหรับบุตรหลาน
|
|
onChildRemoved() |
ฟังรายการที่ถูกนำออกจากลิสต์ โดย
DataSnapshot ที่ส่งไปยังการเรียกกลับของเหตุการณ์จะมี
data สำหรับบุตรหลานที่ถูกนำออก
|
|
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
ระบบจะนำการเรียกกลับออกโดยการเรียกใช้เมธอด removeEventListener()
ในการอ้างอิงฐานข้อมูล Firebase
หากมีการเพิ่ม Listener หลายครั้งไปยังตำแหน่งข้อมูล ระบบจะเรียกใช้ Listener หลายครั้งสำหรับแต่ละเหตุการณ์ และคุณต้องยกเลิกการเชื่อมต่อ Listener จำนวนครั้งเท่ากันเพื่อนำออกโดยสมบูรณ์
การเรียกใช้ removeEventListener()
ใน Listener ขององค์กรระดับบนจะไม่
นำ Listener ที่ลงทะเบียนในโหนดขององค์กรย่อยออกโดยอัตโนมัติ
ต้องเรียกใช้ removeEventListener()
ใน Listener ขององค์กรย่อยด้วย
เพื่อนำการเรียกกลับออก
การจัดเรียงและการกรองข้อมูล
คุณใช้คลาส Realtime Database Query
เพื่อดึงข้อมูลที่จัดเรียงตามคีย์ ตามค่า หรือตามค่าของรายการย่อยได้ นอกจากนี้ คุณยังกรอง
ผลลัพธ์ที่จัดเรียงแล้วให้เหลือจำนวนผลลัพธ์ที่เฉพาะเจาะจงหรือช่วงของคีย์หรือ
ค่าได้ด้วย
จัดเรียงข้อมูล
หากต้องการดึงข้อมูลที่จัดเรียงแล้ว ให้เริ่มโดยการระบุเมธอด order-by อย่างใดอย่างหนึ่งเพื่อ กำหนดวิธีจัดเรียงผลลัพธ์
วิธีการ | การใช้งาน |
---|---|
orderByChild() |
จัดเรียงผลลัพธ์ตามค่าของคีย์ย่อยที่ระบุหรือเส้นทางย่อยที่ซ้อนกัน |
orderByKey()
| จัดเรียงผลลัพธ์ตามคีย์ย่อย |
orderByValue() |
จัดเรียงผลลัพธ์ตามค่าของรายการย่อย |
คุณใช้วิธีการจัดเรียงได้วิธีเดียวในแต่ละครั้ง การเรียกใช้เมธอด Order-by หลายครั้งในคำค้นหาเดียวกันจะทำให้เกิดข้อผิดพลาด
ตัวอย่างต่อไปนี้แสดงวิธีดึงรายการโพสต์ยอดนิยมของผู้ใช้ ที่จัดเรียงตามจำนวนดาว
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 // ... });
ซึ่งจะกำหนดคําค้นหาที่เมื่อรวมกับเครื่องมือฟังย่อย จะซิงค์ไคลเอ็นต์กับโพสต์ของผู้ใช้จากเส้นทางในฐานข้อมูล โดยอิงตามรหัสผู้ใช้ และจัดเรียงตามจํานวนดาวที่แต่ละโพสต์ได้รับ เทคนิคการใช้รหัสเป็นคีย์ดัชนีนี้เรียกว่าการกระจายข้อมูล คุณสามารถอ่านข้อมูลเพิ่มเติมได้ในจัดโครงสร้างฐานข้อมูล
การเรียกใช้เมธอด 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
ได้โดยการระบุเส้นทางที่เกี่ยวข้องไปยังองค์ประกอบย่อยที่ซ้อนกันในการเรียก 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() |
แสดงรายการที่เท่ากับคีย์หรือค่าที่ระบุ โดยขึ้นอยู่กับวิธีการจัดเรียงที่เลือก |
คุณรวมฟังก์ชัน limit หรือ range หลายรายการได้ ซึ่งต่างจากเมธอด order-by
เช่น คุณสามารถรวมเมธอด 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 ในตอนแรกคุณจะได้รับการเรียกกลับ onChildAdded()
สูงสุด 100 รายการเท่านั้น หากคุณมีรายการที่จัดเก็บไว้ในฐานข้อมูล Firebase น้อยกว่า 100 รายการ onChildAdded()
การเรียกกลับจะทริกเกอร์สำหรับแต่ละรายการ
เมื่อมีการเปลี่ยนแปลงในรายการ คุณจะได้รับการเรียกกลับ onChildAdded()
สำหรับรายการที่เข้าสู่คำค้นหา และการเรียกกลับ 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()
ยกเว้นจะใช้ค่าของโหนดแทนค่าของคีย์ย่อยที่ระบุ