เอกสารนี้อธิบายวิธีทํางานกับรายการข้อมูลใน Firebase หากต้องการดูข้อมูลเบื้องต้นเกี่ยวกับการอ่านและเขียนข้อมูล Firebase โปรดดูอ่านและเขียนข้อมูลใน Android
รับ DatabaseReference
หากต้องการอ่านและเขียนข้อมูลจากฐานข้อมูล คุณต้องมีอินสแตนซ์ของ DatabaseReference
ดังนี้
Kotlin+KTX
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
ต่อไปนี้
การส่งแบบฟอร์ม | การติดต่อกลับของเหตุการณ์ | การใช้งานทั่วไป |
---|---|---|
ChildEventListener
| onChildAdded() |
เรียกข้อมูลรายการหรือฟังการเพิ่มเติมรายการ
การเรียกกลับนี้จะทริกเกอร์ 1 ครั้งสําหรับรายการย่อยที่มีอยู่แต่ละรายการ จากนั้นจะทริกเกอร์อีกครั้งทุกครั้งที่มีการเพิ่มรายการย่อยใหม่ลงในเส้นทางที่ระบุ DataSnapshot ที่ส่งไปยัง Listener มีข้อมูลของบุตรหลานใหม่
|
onChildChanged() |
ฟังการเปลี่ยนแปลงรายการในรายการ เหตุการณ์นี้จะทริกเกอร์ทุกครั้งที่มีการปรับแต่งโหนดย่อย รวมถึงการปรับแต่งโหนดย่อยที่สืบทอดมา DataSnapshot ที่ส่งไปยัง Listener เหตุการณ์มีข้อมูลที่อัปเดตแล้วสำหรับบุตร
|
|
onChildRemoved() |
ฟังรายการที่นําออกจากรายการ DataSnapshot ที่ส่งไปยังการเรียกกลับเหตุการณ์มีข้อมูลของบุตรหลานที่นําออก
|
|
onChildMoved() |
ฟังการเปลี่ยนแปลงลําดับของรายการในรายการที่เรียงลําดับ
เหตุการณ์นี้จะทริกเกอร์เมื่อใดก็ตามที่มีการเรียก onChildChanged()
Callback โดยการอัปเดตที่ทําให้จัดเรียงรายการย่อยใหม่
ใช้กับข้อมูลที่จัดเรียงด้วย orderByChild หรือ orderByValue
|
ตัวอย่างเช่น แอปการเขียนบล็อกโซเชียลอาจใช้วิธีการเหล่านี้ร่วมกันเพื่อตรวจสอบกิจกรรมในความคิดเห็นของโพสต์ ดังที่แสดงด้านล่าง
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);
รอรับเหตุการณ์ที่มีมูลค่า
แม้ว่าการใช้ ChildEventListener
เป็นวิธีที่แนะนำในการอ่านรายการข้อมูล แต่ก็มีบางกรณีที่การแนบ ValueEventListener
กับรายการอ้างอิงจะมีประโยชน์
การแนบ ValueEventListener
กับรายการข้อมูลจะแสดงรายการข้อมูลทั้งหมดเป็น DataSnapshot
รายการเดียว จากนั้นคุณจะวนซ้ำเพื่อเข้าถึงรายการย่อยแต่ละรายการได้
แม้จะมีการจับคู่เพียงรายการเดียวสําหรับคําค้นหา แต่ภาพรวมจะยังคงเป็นรายการที่มีเพียงรายการเดียว หากต้องการเข้าถึงรายการ คุณต้องวนดูผลลัพธ์โดยทำดังนี้
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()); // ... } });
รูปแบบนี้มีประโยชน์เมื่อคุณต้องการดึงข้อมูลรายการย่อยทั้งหมดในรายการเดียวในการดําเนินการเดียว แทนที่จะรอเหตุการณ์ onChildAdded
เพิ่มเติม
แยก Listener
คุณนำการเรียกกลับออกได้โดยเรียกใช้เมธอด removeEventListener()
ในข้อมูลอ้างอิงฐานข้อมูล Firebase
หากเพิ่มตัวรับฟังลงในตำแหน่งข้อมูลหลายครั้ง ระบบจะเรียกใช้ตัวรับฟังหลายครั้งสำหรับแต่ละเหตุการณ์ และคุณต้องถอดออกด้วยจำนวนครั้งที่เท่ากันเพื่อนำออกอย่างสมบูรณ์
การเรียก removeEventListener()
ใน Listener หลักจะไม่นำ Listener ที่ลงทะเบียนในโหนดย่อยออกโดยอัตโนมัติ คุณต้องเรียก removeEventListener()
ใน Listener ย่อยด้วยเพื่อนำการเรียกกลับออก
การจัดเรียงและการกรองข้อมูล
คุณสามารถใช้คลาส Realtime Database Query
เพื่อเรียกข้อมูลที่จัดเรียงตามคีย์ ตามค่า หรือตามค่าของรายการย่อย นอกจากนี้ คุณยังกรองผลลัพธ์ที่จัดเรียงตามจำนวนผลลัพธ์ที่ต้องการ หรือช่วงของคีย์หรือค่าได้ด้วย
จัดเรียงข้อมูล
หากต้องการดึงข้อมูลที่จัดเรียงแล้ว ให้เริ่มด้วยการระบุวิธีการ "จัดเรียงตาม" อย่างใดอย่างหนึ่งเพื่อกำหนดวิธีจัดเรียงผลลัพธ์ ดังนี้
วิธีการ | การใช้งาน |
---|---|
orderByChild() |
จัดเรียงผลลัพธ์ตามค่าของคีย์ย่อยที่ระบุหรือเส้นทางย่อยที่ฝังอยู่ |
orderByKey()
| จัดเรียงผลลัพธ์ตามคีย์ย่อย |
orderByValue() |
จัดเรียงผลลัพธ์ตามค่าย่อย |
คุณใช้วิธีการจัดเรียงได้ครั้งละ 1 วิธีเท่านั้น การเรียกใช้เมธอด "order-by" หลายครั้งในคําค้นหาเดียวกันจะทำให้เกิดข้อผิดพลาด
ตัวอย่างต่อไปนี้แสดงวิธีดึงข้อมูลรายการโพสต์ยอดนิยมของผู้ใช้ที่จัดเรียงตามจำนวนดาว
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 // ... });
การดำเนินการนี้จะกำหนดการค้นหาที่เมื่อรวมกับ 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
ได้โดยระบุเส้นทางแบบสัมพัทธ์ไปยังรายการย่อยที่ฝังอยู่ในการเรียกใช้ 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 // ... });
ดูข้อมูลเพิ่มเติมเกี่ยวกับวิธีจัดเรียงข้อมูลประเภทอื่นๆ ได้ที่วิธีจัดเรียงข้อมูลการค้นหา
การกรองข้อมูล
หากต้องการกรองข้อมูล ให้รวมวิธีการจํากัดหรือช่วงกับวิธีการเรียงลําดับเมื่อสร้างคําค้นหา
วิธีการ | การใช้งาน |
---|---|
limitToFirst() |
กำหนดจำนวนรายการสูงสุดที่จะแสดงจากจุดเริ่มต้นของรายการผลลัพธ์ที่จัดเรียง |
limitToLast() |
กำหนดจำนวนรายการสูงสุดที่จะแสดงจากท้ายรายการผลลัพธ์ที่จัดเรียง |
startAt() |
แสดงรายการที่มากกว่าหรือเท่ากับคีย์หรือค่าที่ระบุ โดยขึ้นอยู่กับวิธีการจัดเรียงที่เลือก |
startAfter() |
แสดงผลรายการที่มากกว่าคีย์หรือค่าที่ระบุ โดยขึ้นอยู่กับวิธีการจัดเรียงที่เลือก |
endAt() |
แสดงผลรายการที่น้อยกว่าหรือเท่ากับคีย์หรือค่าที่ระบุ ทั้งนี้ขึ้นอยู่กับวิธีการจัดเรียงที่เลือก |
endBefore() |
แสดงผลรายการที่น้อยกว่าคีย์หรือค่าที่ระบุ ทั้งนี้ขึ้นอยู่กับวิธีการจัดเรียงที่เลือก |
equalTo() |
แสดงผลรายการที่เท่ากับคีย์หรือค่าที่ระบุ โดยขึ้นอยู่กับวิธีการจัดเรียงที่เลือก |
คุณรวมฟังก์ชันการจำกัดหรือช่วงหลายรายการเข้าด้วยกันได้ ซึ่งแตกต่างจากเมธอด order-by
เช่น คุณสามารถรวมเมธอด startAt()
และ endAt()
เพื่อจํากัดผลลัพธ์ให้อยู่ในช่วงค่าที่ระบุ
แม้จะจับคู่คำค้นหาได้เพียงรายการเดียว สแนปชอตจะยังคงเป็นลิสต์ที่มีเพียงรายการเดียว หากต้องการเข้าถึงรายการ คุณต้องวนซ้ำผลลัพธ์ ดังนี้
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()); // ... } });
จำกัดจำนวนผลลัพธ์
คุณสามารถใช้เมธอด limitToFirst()
และ limitToLast()
เพื่อตั้งค่าจำนวนรายการย่อยสูงสุดที่จะซิงค์สําหรับการเรียกกลับหนึ่งๆ เช่น หากใช้ limitToFirst()
เพื่อตั้งค่าขีดจํากัดเป็น 100 รายการ ในช่วงแรกคุณจะได้รับ onChildAdded()
แคลลบ์แบ็กสูงสุด 100 รายการเท่านั้น หากคุณมีรายการที่จัดเก็บในฐานข้อมูล Firebase น้อยกว่า 100 รายการ ระบบจะเรียกใช้การเรียกกลับ onChildAdded()
สำหรับแต่ละรายการ
เมื่อรายการมีการเปลี่ยนแปลง คุณจะได้รับ onChildAdded()
การเรียกกลับสำหรับรายการที่เข้าสู่การค้นหา และ onChildRemoved()
การเรียกกลับสำหรับรายการที่ออกจากการค้นหาเพื่อให้จำนวนทั้งหมดยังคงอยู่ที่ 100
ตัวอย่างต่อไปนี้แสดงวิธีที่แอปการเขียนบล็อกตัวอย่างกำหนดการค้นหาเพื่อดึงข้อมูลรายการโพสต์ล่าสุด 100 รายการของผู้ใช้ทั้งหมด
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);
ตัวอย่างนี้กําหนดคําค้นหาเท่านั้น หากต้องการซิงค์ข้อมูลจริง จะต้องมีโปรแกรมรับฟังที่แนบอยู่
กรองตามคีย์หรือค่า
คุณสามารถใช้ startAt()
, startAfter()
, endAt()
, endBefore()
และ equalTo()
เพื่อเลือกจุดเริ่มต้น จุดสิ้นสุด และจุดที่เทียบเท่าแบบกำหนดเองสำหรับข้อความค้นหา ซึ่งอาจเป็นประโยชน์สำหรับการแบ่งหน้าข้อมูลหรือค้นหารายการที่มีรายการย่อยซึ่งมีค่าที่เฉพาะเจาะจง
วิธีจัดเรียงข้อมูลการค้นหา
ส่วนนี้จะอธิบายวิธีจัดเรียงข้อมูลตามเมธอด order-by แต่ละรายการในคลาส Query
orderByChild
เมื่อใช้ orderByChild()
ระบบจะจัดเรียงข้อมูลที่มีคีย์ย่อยที่ระบุดังนี้
- รายการย่อยที่มีค่า
null
สำหรับคีย์ย่อยที่ระบุจะแสดงก่อน - รายการย่อยที่มีค่าเป็น
false
สำหรับคีย์ย่อยที่ระบุจะแสดงต่อจากนี้ หากรายการย่อยหลายรายการมีค่าเป็นfalse
ระบบจะจัดเรียงตามลําดับตัวอักษรตามคีย์ - รายการย่อยที่มีค่าเป็น
true
สำหรับคีย์ย่อยที่ระบุจะแสดงต่อจากนี้ หากรายการย่อยหลายรายการมีค่าเป็นtrue
ระบบจะจัดเรียงตามลําดับตัวอักษรตามคีย์ - รายการย่อยที่มีค่าตัวเลขจะแสดงต่อจากนี้โดยจัดเรียงจากน้อยไปมาก หากโหนดย่อยหลายรายการมีค่าตัวเลขเหมือนกันสำหรับโหนดย่อยที่ระบุ ระบบจะจัดเรียงตามคีย์
- สตริงจะอยู่หลังตัวเลขและจัดเรียงตามลําดับตัวอักษรจากน้อยไปมาก หากโหนดย่อยหลายรายการมีค่าเดียวกันสำหรับโหนดย่อยที่ระบุ ระบบจะจัดเรียงตามลําดับตัวอักษรตามคีย์
- ออบเจ็กต์จะอยู่ท้ายสุดและจัดเรียงตามลําดับตัวอักษรตามคีย์จากน้อยไปมาก
orderByKey
เมื่อใช้ orderByKey()
เพื่อจัดเรียงข้อมูล ระบบจะแสดงผลข้อมูลตามลําดับจากน้อยไปมากตามคีย์
- รายการย่อยที่มีคีย์ที่แยกวิเคราะห์เป็นจำนวนเต็ม 32 บิตจะแสดงก่อน โดยจัดเรียงจากน้อยไปมาก
- รายการย่อยที่มีค่าสตริงเป็นคีย์จะแสดงต่อจากนี้ โดยจัดเรียงตามลําดับตัวอักษรจากน้อยไปมาก
orderByValue
เมื่อใช้ orderByValue()
ระบบจะจัดเรียงรายการย่อยตามค่า เกณฑ์การจัดเรียงจะเหมือนกับใน orderByChild()
ยกเว้นจะใช้ค่าของโหนดแทนค่าของคีย์ย่อยที่ระบุ