เอกสารนี้อธิบายวิธีทํางานกับรายการข้อมูลใน Firebase หากต้องการดูข้อมูลเบื้องต้นเกี่ยวกับการอ่านและเขียนข้อมูล Firebase โปรดดูอ่านและเขียนข้อมูลใน Android
รับ DatabaseReference
หากต้องการอ่านและเขียนข้อมูลจากฐานข้อมูล คุณต้องมีอินสแตนซ์ของ DatabaseReference
ดังนี้
private lateinit var database: DatabaseReference // ... database = Firebase.database.reference
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()
ทริกเกอร์โดยการอัปเดตที่ทําให้จัดเรียงรายการย่อยใหม่
ใช้กับข้อมูลที่จัดเรียงด้วย orderByChild หรือ orderByValue
|
ตัวอย่างเช่น แอปการเขียนบล็อกโซเชียลอาจใช้วิธีการเหล่านี้ร่วมกันเพื่อตรวจสอบกิจกรรมในความคิดเห็นของโพสต์ ดังที่แสดงด้านล่าง
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)
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
รายการเดียว จากนั้นคุณจะวนซ้ำเพื่อเข้าถึงรายการย่อยแต่ละรายการได้
แม้จะมีการจับคู่เพียงรายการเดียวสําหรับคําค้นหา แต่ภาพรวมจะยังคงเป็นรายการที่มีเพียงรายการเดียว หากต้องการเข้าถึงรายการ คุณต้องวนผ่านผลลัพธ์ ดังนี้
// 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()) // ... } })
// 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" หลายครั้งในคําค้นหาเดียวกันจะทำให้เกิดข้อผิดพลาด
ตัวอย่างต่อไปนี้แสดงวิธีดึงข้อมูลรายการโพสต์ยอดนิยมของผู้ใช้ที่จัดเรียงตามจำนวนดาว
// 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 // ... })
// 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()
// Most viewed posts val myMostViewedPostsQuery = databaseReference.child("posts") .orderByChild("metrics/views") myMostViewedPostsQuery.addChildEventListener(object : ChildEventListener { // TODO: implement the ChildEventListener methods as documented above // ... })
// Most viewed posts Query myMostViewedPostsQuery = databaseReference.child("posts") .orderByChild("metrics/views"); myMostViewedPostsQuery.addChildEventListener(new ChildEventListener() { // TODO: implement the ChildEventListener methods as documented above // ... });
ดูข้อมูลเพิ่มเติมเกี่ยวกับการจัดเรียงข้อมูลประเภทอื่นๆ ได้ที่วิธีจัดเรียงข้อมูลการค้นหา
การกรองข้อมูล
หากต้องการกรองข้อมูล ให้รวมเมธอด limit หรือ range กับเมธอด order-by เมื่อสร้างการค้นหา
วิธีการ | การใช้งาน |
---|---|
limitToFirst() |
กำหนดจำนวนรายการสูงสุดที่จะแสดงจากจุดเริ่มต้นของรายการผลลัพธ์ที่จัดเรียง |
limitToLast() |
กำหนดจำนวนรายการสูงสุดที่จะแสดงจากท้ายรายการผลลัพธ์ที่จัดเรียง |
startAt() |
แสดงรายการที่มากกว่าหรือเท่ากับคีย์หรือค่าที่ระบุ โดยขึ้นอยู่กับวิธีการจัดเรียงที่เลือก |
startAfter() |
แสดงผลรายการที่มากกว่าคีย์หรือค่าที่ระบุ โดยขึ้นอยู่กับวิธีการจัดเรียงที่เลือก |
endAt() |
แสดงรายการที่น้อยกว่าหรือเท่ากับคีย์หรือค่าที่ระบุ ทั้งนี้ขึ้นอยู่กับวิธีการจัดเรียงที่เลือก |
endBefore() |
แสดงผลรายการที่น้อยกว่าคีย์หรือค่าที่ระบุ ทั้งนี้ขึ้นอยู่กับวิธีการจัดเรียงที่เลือก |
equalTo() |
แสดงผลรายการที่เท่ากับคีย์หรือค่าที่ระบุ โดยขึ้นอยู่กับวิธีการจัดเรียงที่เลือก |
คุณรวมฟังก์ชันการจำกัดหรือช่วงหลายรายการเข้าด้วยกันได้ ซึ่งแตกต่างจากเมธอด order-by
เช่น คุณสามารถรวมเมธอด startAt()
และ endAt()
เพื่อจํากัดผลลัพธ์ให้อยู่ในช่วงค่าที่ระบุ
แม้จะจับคู่คำค้นหาได้เพียงรายการเดียว สแนปชอตจะยังคงเป็นลิสต์ที่มีเพียงรายการเดียว หากต้องการเข้าถึงรายการดังกล่าว คุณต้องวนลูปผลลัพธ์ ดังนี้
// 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()) // ... } })
// 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 รายการของผู้ใช้ทั้งหมด
// Last 100 posts, these are automatically the 100 most recent // due to sorting by push() keys. databaseReference.child("posts").limitToFirst(100)
// 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()
ยกเว้นจะใช้ค่าของโหนดแทนค่าของคีย์ย่อยที่ระบุ