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