เอกสารนี้ครอบคลุมข้อมูลพื้นฐานเกี่ยวกับการดึงข้อมูลฐานข้อมูล วิธีจัดเรียงข้อมูล และวิธีดำเนินการค้นหาข้อมูลอย่างง่าย การดึงข้อมูลใน Admin SDK จะใช้ภาษาโปรแกรมที่แตกต่างกันเล็กน้อย
- Listener แบบอะซิงโครนัส: ระบบจะดึงข้อมูลที่จัดเก็บไว้ใน Firebase Realtime Database โดยแนบ Listener แบบอะซิงโครนัสกับการอ้างอิงฐานข้อมูล ระบบจะทริกเกอร์ Listener 1 ครั้งสำหรับสถานะเริ่มต้นของข้อมูล และจะทริกเกอร์อีกครั้งเมื่อข้อมูลมีการเปลี่ยนแปลง Listener เหตุการณ์อาจได้รับประเภทเหตุการณ์ที่แตกต่างกันหลายประเภท โหมดการดึงข้อมูลนี้รองรับใน Admin SDK ของ Java, Node.js และ Python
- การอ่าน Blocking: ระบบจะดึงข้อมูลที่จัดเก็บไว้ใน Firebase Realtime Database โดยเรียกใช้เมธอด Blocking ในการอ้างอิงฐานข้อมูล ซึ่งจะแสดงผลข้อมูลที่จัดเก็บไว้ในการอ้างอิง การเรียกใช้เมธอดแต่ละครั้งเป็นการดำเนินการแบบครั้งเดียว ซึ่งหมายความว่า SDK จะไม่ลงทะเบียน Callback ที่รอการอัปเดตข้อมูลในภายหลัง Admin SDK ของ Python และ Go รองรับโมเดลการดึงข้อมูลนี้
เริ่มต้นใช้งาน
ลองกลับมาดูตัวอย่างการเขียนบล็อกในบทความก่อนหน้าเพื่อทำความเข้าใจวิธีอ่านข้อมูลจากฐานข้อมูล Firebase โปรดทราบว่าบล็อกโพสต์ในแอปตัวอย่างจะจัดเก็บไว้ที่ URL ของฐานข้อมูล https://docs-examples.firebaseio.com/server/saving-data/fireblog/posts.json หากต้องการอ่านข้อมูลโพสต์ ให้ทำดังนี้
Java
public static class Post { public String author; public String title; public Post(String author, String title) { // ... } } // Get a reference to our posts final FirebaseDatabase database = FirebaseDatabase.getInstance(); DatabaseReference ref = database.getReference("server/saving-data/fireblog/posts"); // Attach a listener to read the data at our posts reference ref.addValueEventListener(new ValueEventListener() { @Override public void onDataChange(DataSnapshot dataSnapshot) { Post post = dataSnapshot.getValue(Post.class); System.out.println(post); } @Override public void onCancelled(DatabaseError databaseError) { System.out.println("The read failed: " + databaseError.getCode()); } });
Node.js
// Get a database reference to our posts const db = getDatabase(); const ref = db.ref('server/saving-data/fireblog/posts'); // Attach an asynchronous callback to read the data at our posts reference ref.on('value', (snapshot) => { console.log(snapshot.val()); }, (errorObject) => { console.log('The read failed: ' + errorObject.name); });
Python
# Import database module. from firebase_admin import db # Get a database reference to our posts ref = db.reference('server/saving-data/fireblog/posts') # Read the data at the posts reference (this is a blocking operation) print(ref.get())
Go
// Post is a json-serializable type. type Post struct { Author string `json:"author,omitempty"` Title string `json:"title,omitempty"` } // Create a database client from App. client, err := app.Database(ctx) if err != nil { log.Fatalln("Error initializing database client:", err) } // Get a database reference to our posts ref := client.NewRef("server/saving-data/fireblog/posts") // Read the data at the posts reference (this is a blocking operation) var post Post if err := ref.Get(ctx, &post); err != nil { log.Fatalln("Error reading value:", err) }
หากเรียกใช้โค้ดด้านบน คุณจะเห็นออบเจ็กต์ที่มีโพสต์ทั้งหมดที่บันทึกไว้ในคอนโซล ในกรณีของ Node.js และ Java ระบบจะเรียกใช้ฟังก์ชัน Listener ทุกครั้งที่มีการเพิ่มข้อมูลใหม่ลงในการอ้างอิงฐานข้อมูล และคุณไม่จำเป็นต้องเขียนโค้ดเพิ่มเติมเพื่อให้เกิดเหตุการณ์นี้
ใน Java และ Node.js ฟังก์ชัน Callback จะได้รับ DataSnapshot ซึ่งเป็นสแนปชอตของข้อมูล ภาพรวมคือรูปภาพของข้อมูลที่การอ้างอิงฐานข้อมูลหนึ่งๆ ณ จุดเวลาเดียว การเรียกใช้ val() / getValue() ในสแนปชอตจะแสดงผลการนำเสนอออบเจ็กต์ของข้อมูลที่เจาะจงภาษา หากไม่มีข้อมูลในตำแหน่งของข้อมูลอ้างอิง ค่าของสแนปชอตจะเป็น null เมธอด get() ใน Python จะแสดงผลการนำเสนอข้อมูลของ Python โดยตรง ฟังก์ชัน Get() ใน Go จะยกเลิกการจัดระเบียบข้อมูลให้เป็นโครงสร้างข้อมูลที่กำหนด
โปรดทราบว่าเราใช้ประเภทเหตุการณ์ value ในตัวอย่างด้านบน ซึ่งจะอ่านเนื้อหาทั้งหมดของการอ้างอิงฐานข้อมูล Firebase แม้ว่าจะมีข้อมูลเปลี่ยนแปลงเพียงรายการเดียวก็ตาม value เป็นหนึ่งในเหตุการณ์ 5 ประเภทที่แสดงด้านล่างซึ่งคุณใช้เพื่ออ่านข้อมูลจากฐานข้อมูลได้
อ่านประเภทเหตุการณ์ใน Java และ Node.js
ค่า
ระบบจะใช้เหตุการณ์ value เพื่ออ่านภาพรวมแบบคงที่ของเนื้อหาในเส้นทางฐานข้อมูลที่ระบุตามที่มีอยู่ในขณะที่เกิดเหตุการณ์การอ่าน โดยจะทริกเกอร์ 1 ครั้งด้วยข้อมูลเริ่มต้น และทริกเกอร์อีกครั้งทุกครั้งที่ข้อมูลมีการเปลี่ยนแปลง ระบบจะส่ง Callback ของเหตุการณ์ไปยังสแนปชอตที่มีข้อมูลทั้งหมดที่ตำแหน่งนั้น รวมถึงข้อมูลย่อย ในตัวอย่างโค้ดด้านบน value แสดงผลบล็อกโพสต์ทั้งหมดในแอปของคุณ ทุกครั้งที่มีการเพิ่มบล็อกโพสต์ใหม่ ฟังก์ชัน Callback จะแสดงผลโพสต์ทั้งหมด
เพิ่มบุตรหลานแล้ว
โดยทั่วไปแล้ว ระบบจะใช้เหตุการณ์ child_added เมื่อดึงข้อมูลรายการสินค้าจากฐานข้อมูล child_added จะทำงานหนึ่งครั้งสำหรับออบเจ็กต์ย่อยที่มีอยู่แต่ละรายการ และจะทำงานอีกครั้งทุกครั้งที่มีการเพิ่มออบเจ็กต์ย่อยใหม่ลงในเส้นทางที่ระบุ ซึ่งแตกต่างจาก value ที่แสดงเนื้อหาทั้งหมดของตำแหน่ง ระบบจะส่งภาพรวมที่มีข้อมูลของรายการย่อยใหม่ไปยัง Callback ของเหตุการณ์ สำหรับการจัดลำดับ ระบบจะส่งอาร์กิวเมนต์ที่ 2 ซึ่งมีคีย์ของรายการย่อยก่อนหน้าด้วย
หากต้องการดึงเฉพาะข้อมูลในโพสต์ใหม่แต่ละรายการที่เพิ่มลงในแอปบล็อก คุณสามารถใช้ child_added ดังนี้
Java
ref.addChildEventListener(new ChildEventListener() { @Override public void onChildAdded(DataSnapshot dataSnapshot, String prevChildKey) { Post newPost = dataSnapshot.getValue(Post.class); System.out.println("Author: " + newPost.author); System.out.println("Title: " + newPost.title); System.out.println("Previous Post ID: " + prevChildKey); } @Override public void onChildChanged(DataSnapshot dataSnapshot, String prevChildKey) {} @Override public void onChildRemoved(DataSnapshot dataSnapshot) {} @Override public void onChildMoved(DataSnapshot dataSnapshot, String prevChildKey) {} @Override public void onCancelled(DatabaseError databaseError) {} });
Node.js
// Retrieve new posts as they are added to our database ref.on('child_added', (snapshot, prevChildKey) => { const newPost = snapshot.val(); console.log('Author: ' + newPost.author); console.log('Title: ' + newPost.title); console.log('Previous Post ID: ' + prevChildKey); });
ในตัวอย่างนี้ สแนปชอตจะมีออบเจ็กต์ที่มีบล็อกโพสต์แต่ละรายการ เนื่องจาก SDK จะแปลงโพสต์เป็นออบเจ็กต์โดยการดึงค่า คุณจึงเข้าถึงพร็อพเพอร์ตี้ผู้เขียนและชื่อของโพสต์ได้โดยเรียกใช้ author และ title ตามลำดับ นอกจากนี้ คุณยังมีสิทธิ์เข้าถึงรหัสโพสต์ก่อนหน้าจากอาร์กิวเมนต์ prevChildKey ที่ 2 ด้วย
เปลี่ยนรายการย่อยแล้ว
ระบบจะทริกเกอร์เหตุการณ์ child_changed ทุกครั้งที่มีการแก้ไขโหนดย่อย ซึ่งรวมถึงการแก้ไขโหนดสืบทอดของโหนดบุตรหลาน โดยปกติแล้วจะใช้ร่วมกับ child_added และ child_removed เพื่อตอบสนองต่อการเปลี่ยนแปลงในรายการสินค้า สแนปชอตที่ส่งไปยัง Callback ของเหตุการณ์จะมีข้อมูลที่อัปเดตสำหรับโหนดบุตรหลาน
คุณสามารถใช้ child_changed เพื่ออ่านข้อมูลที่อัปเดตในบล็อกโพสต์เมื่อมีการแก้ไขได้ ดังนี้
Java
ref.addChildEventListener(new ChildEventListener() { @Override public void onChildAdded(DataSnapshot dataSnapshot, String prevChildKey) {} @Override public void onChildChanged(DataSnapshot dataSnapshot, String prevChildKey) { Post changedPost = dataSnapshot.getValue(Post.class); System.out.println("The updated post title is: " + changedPost.title); } @Override public void onChildRemoved(DataSnapshot dataSnapshot) {} @Override public void onChildMoved(DataSnapshot dataSnapshot, String prevChildKey) {} @Override public void onCancelled(DatabaseError databaseError) {} });
Node.js
// Get the data on a post that has changed ref.on('child_changed', (snapshot) => { const changedPost = snapshot.val(); console.log('The updated post title is ' + changedPost.title); });
นำบุตรหลานออกแล้ว
ระบบจะทริกเกอร์เหตุการณ์ child_removed เมื่อมีการนำองค์ประกอบย่อยระดับแรกออก โดยปกติแล้วจะใช้ร่วมกับ child_added และ child_changed สแนปชอตที่ส่งไปยัง Callback ของเหตุการณ์จะมีข้อมูลสำหรับรายการย่อยที่ถูกนำออก
ในตัวอย่างบล็อก คุณสามารถใช้ child_removed เพื่อบันทึกการแจ้งเตือนเกี่ยวกับโพสต์ที่ลบไปยังคอนโซลได้ ดังนี้
Java
ref.addChildEventListener(new ChildEventListener() { @Override public void onChildAdded(DataSnapshot dataSnapshot, String prevChildKey) {} @Override public void onChildChanged(DataSnapshot dataSnapshot, String prevChildKey) {} @Override public void onChildRemoved(DataSnapshot dataSnapshot) { Post removedPost = dataSnapshot.getValue(Post.class); System.out.println("The blog post titled " + removedPost.title + " has been deleted"); } @Override public void onChildMoved(DataSnapshot dataSnapshot, String prevChildKey) {} @Override public void onCancelled(DatabaseError databaseError) {} });
Node.js
// Get a reference to our posts const ref = db.ref('server/saving-data/fireblog/posts'); // Get the data on a post that has been removed ref.on('child_removed', (snapshot) => { const deletedPost = snapshot.val(); console.log('The blog post titled \'' + deletedPost.title + '\' has been deleted'); });
ย้ายบุตรหลานแล้ว
ระบบจะใช้เหตุการณ์ child_moved เมื่อทำงานกับข้อมูลที่เรียงลำดับ ซึ่งจะอธิบายไว้ในส่วนถัดไป
การรับประกันกิจกรรม
ฐานข้อมูล Firebase มีการรับประกันที่สำคัญหลายประการเกี่ยวกับเหตุการณ์ ดังนี้
| การรับประกันเหตุการณ์ในฐานข้อมูล |
|---|
| ระบบจะทริกเกอร์เหตุการณ์เสมอเมื่อสถานะในเครื่องมีการเปลี่ยนแปลง |
| ในท้ายที่สุดแล้ว เหตุการณ์จะแสดงสถานะที่ถูกต้องของข้อมูลเสมอ แม้ในกรณีที่การดำเนินการในเครื่องหรือเวลาทำให้เกิดความแตกต่างชั่วคราว เช่น การสูญเสียการเชื่อมต่อเครือข่ายชั่วคราว |
| การเขียนจากไคลเอ็นต์เดียวจะเขียนไปยังเซิร์ฟเวอร์และเผยแพร่ไปยังผู้ใช้รายอื่นตามลำดับเสมอ |
| เหตุการณ์ค่าจะทริกเกอร์เป็นเหตุการณ์สุดท้ายเสมอ และรับประกันว่าจะมีข้อมูลอัปเดตจากเหตุการณ์อื่นๆ ที่เกิดขึ้นก่อนที่จะมีการจับภาพสแนปชอตนั้น |
เนื่องจากเหตุการณ์ค่าจะทริกเกอร์เป็นรายการสุดท้ายเสมอ ตัวอย่างต่อไปนี้จึงทํางานเสมอ
Java
final AtomicInteger count = new AtomicInteger(); ref.addChildEventListener(new ChildEventListener() { @Override public void onChildAdded(DataSnapshot dataSnapshot, String prevChildKey) { // New child added, increment count int newCount = count.incrementAndGet(); System.out.println("Added " + dataSnapshot.getKey() + ", count is " + newCount); } // ... }); // The number of children will always be equal to 'count' since the value of // the dataSnapshot here will include every child_added event triggered before this point. ref.addListenerForSingleValueEvent(new ValueEventListener() { @Override public void onDataChange(DataSnapshot dataSnapshot) { long numChildren = dataSnapshot.getChildrenCount(); System.out.println(count.get() + " == " + numChildren); } @Override public void onCancelled(DatabaseError databaseError) {} });
Node.js
let count = 0; ref.on('child_added', (snap) => { count++; console.log('added:', snap.key); }); // length will always equal count, since snap.val() will include every child_added event // triggered before this point ref.once('value', (snap) => { console.log('initial data loaded!', snap.numChildren() === count); });
การแยก Callback
ระบบจะนำ Callback ออกโดยระบุประเภทเหตุการณ์และฟังก์ชัน Callback ที่จะนำออก ดังนี้
Java
// Create and attach listener ValueEventListener listener = new ValueEventListener() { // ... }; ref.addValueEventListener(listener); // Remove listener ref.removeEventListener(listener);
Node.js
ref.off('value', originalCallback);
หากคุณส่งบริบทขอบเขตไปยัง on() คุณต้องส่งบริบทดังกล่าวเมื่อแยก Callback ออก ดังนี้
Java
// Not applicable for JavaNode.js
ref.off('value', originalCallback, ctx);
หากต้องการนำ Callback ทั้งหมดในสถานที่ตั้งออก ให้ทำดังนี้
Java
// No Java equivalent, listeners must be removed individually.Node.js
// Remove all value callbacks ref.off('value'); // Remove all callbacks of any type ref.off();
การอ่านข้อมูลครั้งเดียว
ในบางกรณี การเรียกใช้ Callback เพียงครั้งเดียวแล้วนำออกทันทีอาจเป็นประโยชน์ เราได้สร้างฟังก์ชันตัวช่วยเพื่อให้ดำเนินการนี้ได้ง่ายๆ ดังนี้
Java
ref.addListenerForSingleValueEvent(new ValueEventListener() { @Override public void onDataChange(DataSnapshot dataSnapshot) { // ... } @Override public void onCancelled(DatabaseError databaseError) { // ... } });
Node.js
ref.once('value', (data) => { // do some stuff once });
Python
# Import database module. from firebase_admin import db # Get a database reference to our posts ref = db.reference('server/saving-data/fireblog/posts') # Read the data at the posts reference (this is a blocking operation) print(ref.get())
Go
// Create a database client from App. client, err := app.Database(ctx) if err != nil { log.Fatalln("Error initializing database client:", err) } // Get a database reference to our posts ref := client.NewRef("server/saving-data/fireblog/posts") // Read the data at the posts reference (this is a blocking operation) var post Post if err := ref.Get(ctx, &post); err != nil { log.Fatalln("Error reading value:", err) }
การค้นหาข้อมูล
การค้นหาฐานข้อมูล Firebase ช่วยให้คุณเลือกดึงข้อมูลตามปัจจัยต่างๆ ได้ หากต้องการสร้างการค้นหาในฐานข้อมูล ให้เริ่มด้วยการระบุวิธีที่คุณต้องการจัดเรียงข้อมูลโดยใช้ฟังก์ชันการจัดเรียงอย่างใดอย่างหนึ่งต่อไปนี้ orderByChild(), orderByKey() หรือ orderByValue() จากนั้นคุณสามารถรวมโอเปอเรเตอร์เหล่านี้เข้ากับอีก 5 วิธีเพื่อทำการค้นหาที่ซับซ้อน ได้แก่ limitToFirst(), limitToLast(), startAt(), endAt() และ equalTo()
เนื่องจากพวกเราทุกคนที่ Firebase คิดว่าไดโนเสาร์เป็นสัตว์ที่น่าสนใจ เราจึงจะใช้ข้อมูลโค้ดจากฐานข้อมูลตัวอย่างเกี่ยวกับข้อเท็จจริงของไดโนเสาร์เพื่อสาธิตวิธีค้นหาข้อมูลในฐานข้อมูล Firebase ดังนี้
{
"lambeosaurus": {
"height" : 2.1,
"length" : 12.5,
"weight": 5000
},
"stegosaurus": {
"height" : 4,
"length" : 9,
"weight" : 2500
}
}คุณจัดเรียงข้อมูลได้ 3 วิธี ได้แก่ ตามคีย์ย่อย ตามคีย์ หรือตามค่า การค้นหาฐานข้อมูลพื้นฐานจะเริ่มต้นด้วยฟังก์ชันการจัดลำดับอย่างใดอย่างหนึ่งต่อไปนี้ ซึ่งแต่ละฟังก์ชันจะอธิบายไว้ด้านล่าง
การจัดเรียงตามคีย์ย่อยที่ระบุ
คุณสามารถจัดเรียงโหนดตามคีย์ย่อยทั่วไปได้โดยส่งคีย์นั้นไปยัง orderByChild() ตัวอย่างเช่น หากต้องการอ่านไดโนเสาร์ทั้งหมดที่จัดเรียงตามความสูง คุณสามารถทำดังนี้
Java
public static class Dinosaur { public int height; public int weight; public Dinosaur(int height, int weight) { // ... } } final DatabaseReference dinosaursRef = database.getReference("dinosaurs"); dinosaursRef.orderByChild("height").addChildEventListener(new ChildEventListener() { @Override public void onChildAdded(DataSnapshot dataSnapshot, String prevChildKey) { Dinosaur dinosaur = dataSnapshot.getValue(Dinosaur.class); System.out.println(dataSnapshot.getKey() + " was " + dinosaur.height + " meters tall."); } // ... });
Node.js
const ref = db.ref('dinosaurs'); ref.orderByChild('height').on('child_added', (snapshot) => { console.log(snapshot.key + ' was ' + snapshot.val().height + ' meters tall'); });
Python
ref = db.reference('dinosaurs') snapshot = ref.order_by_child('height').get() for key, val in snapshot.items(): print(f'{key} was {val} meters tall')
Go
// Dinosaur is a json-serializable type. type Dinosaur struct { Height int `json:"height"` Width int `json:"width"` } ref := client.NewRef("dinosaurs") results, err := ref.OrderByChild("height").GetOrdered(ctx) if err != nil { log.Fatalln("Error querying database:", err) } for _, r := range results { var d Dinosaur if err := r.Unmarshal(&d); err != nil { log.Fatalln("Error unmarshaling result:", err) } fmt.Printf("%s was %d meteres tall", r.Key(), d.Height) }
โหนดใดก็ตามที่ไม่มีคีย์ย่อยที่เราค้นหาจะจัดเรียงด้วยค่า null ซึ่งหมายความว่าจะอยู่เป็นอันดับแรกในการจัดเรียง ดูรายละเอียดเกี่ยวกับวิธีจัดเรียงข้อมูลได้ที่ส่วนวิธีจัดเรียงข้อมูล
นอกจากนี้ คุณยังจัดเรียงการค้นหาตามรายการย่อยที่ซ้อนกันหลายระดับได้ด้วย แทนที่จะจัดเรียงตามรายการย่อยที่อยู่ต่ำลงไปเพียงระดับเดียว วิธีนี้มีประโยชน์ในกรณีที่คุณมีข้อมูลที่ซ้อนกันหลายชั้น เช่น
{
"lambeosaurus": {
"dimensions": {
"height" : 2.1,
"length" : 12.5,
"weight": 5000
}
},
"stegosaurus": {
"dimensions": {
"height" : 4,
"length" : 9,
"weight" : 2500
}
}
}หากต้องการค้นหาความสูงในตอนนี้ คุณสามารถใช้เส้นทางแบบเต็มไปยังออบเจ็กต์แทนคีย์เดียวได้ ดังนี้
Java
dinosaursRef.orderByChild("dimensions/height").addChildEventListener(new ChildEventListener() { @Override public void onChildAdded(DataSnapshot dataSnapshot, String prevChildKey) { // ... } // ... });
Node.js
const ref = db.ref('dinosaurs'); ref.orderByChild('dimensions/height').on('child_added', (snapshot) => { console.log(snapshot.key + ' was ' + snapshot.val().height + ' meters tall'); });
Python
ref = db.reference('dinosaurs') snapshot = ref.order_by_child('dimensions/height').get() for key, val in snapshot.items(): print(f'{key} was {val} meters tall')
Go
ref := client.NewRef("dinosaurs") results, err := ref.OrderByChild("dimensions/height").GetOrdered(ctx) if err != nil { log.Fatalln("Error querying database:", err) } for _, r := range results { var d Dinosaur if err := r.Unmarshal(&d); err != nil { log.Fatalln("Error unmarshaling result:", err) } fmt.Printf("%s was %d meteres tall", r.Key(), d.Height) }
การค้นหาสามารถจัดเรียงตามคีย์ได้ครั้งละ 1 คีย์เท่านั้น การเรียกใช้ orderByChild() หลายครั้งในการค้นหาเดียวกันจะทำให้เกิดข้อผิดพลาด
การจัดเรียงตามคีย์
นอกจากนี้ คุณยังจัดเรียงโหนดตามคีย์ได้โดยใช้เมธอด orderByKey() ตัวอย่างต่อไปนี้จะอ่านไดโนเสาร์ทั้งหมดตามลำดับตัวอักษร
Java
dinosaursRef.orderByKey().addChildEventListener(new ChildEventListener() { @Override public void onChildAdded(DataSnapshot dataSnapshot, String prevChildKey) { System.out.println(dataSnapshot.getKey()); } // ... });
Node.js
var ref = db.ref('dinosaurs'); ref.orderByKey().on('child_added', (snapshot) => { console.log(snapshot.key); });
Python
ref = db.reference('dinosaurs') snapshot = ref.order_by_key().get() print(snapshot)
Go
ref := client.NewRef("dinosaurs") results, err := ref.OrderByKey().GetOrdered(ctx) if err != nil { log.Fatalln("Error querying database:", err) } snapshot := make([]Dinosaur, len(results)) for i, r := range results { var d Dinosaur if err := r.Unmarshal(&d); err != nil { log.Fatalln("Error unmarshaling result:", err) } snapshot[i] = d } fmt.Println(snapshot)
การจัดเรียงตามค่า
คุณจัดเรียงโหนดตามค่าของคีย์ย่อยได้โดยใช้เมธอด orderByValue() สมมติว่าไดโนเสาร์กำลังแข่งขันกีฬาไดโนเสาร์ และคุณกำลังติดตามคะแนนของไดโนเสาร์ในรูปแบบต่อไปนี้
{
"scores": {
"bruhathkayosaurus" : 55,
"lambeosaurus" : 21,
"linhenykus" : 80,
"pterodactyl" : 93,
"stegosaurus" : 5,
"triceratops" : 22
}
}หากต้องการจัดเรียงไดโนเสาร์ตามคะแนน คุณสามารถสร้างการค้นหาต่อไปนี้
Java
DatabaseReference scoresRef = database.getReference("scores"); scoresRef.orderByValue().addChildEventListener(new ChildEventListener() { @Override public void onChildAdded(DataSnapshot dataSnapshot, String prevChildKey) { System.out.println("The " + dataSnapshot.getKey() + " score is " + dataSnapshot.getValue()); } // ... });
Node.js
const scoresRef = db.ref('scores'); scoresRef.orderByValue().on('value', (snapshot) => { snapshot.forEach((data) => { console.log('The ' + data.key + ' dinosaur\'s score is ' + data.val()); }); });
Python
ref = db.reference('scores') snapshot = ref.order_by_value().get() for key, val in snapshot.items(): print(f'The {key} dinosaur\'s score is {val}')
Go
ref := client.NewRef("scores") results, err := ref.OrderByValue().GetOrdered(ctx) if err != nil { log.Fatalln("Error querying database:", err) } for _, r := range results { var score int if err := r.Unmarshal(&score); err != nil { log.Fatalln("Error unmarshaling result:", err) } fmt.Printf("The %s dinosaur's score is %d\n", r.Key(), score) }
ดูส่วนวิธีจัดเรียงข้อมูลเพื่อดูคำอธิบายเกี่ยวกับวิธีจัดเรียงค่า null, บูลีน, สตริง และออบเจ็กต์เมื่อใช้ orderByValue()
คำถามที่ซับซ้อน
เมื่อทราบวิธีจัดเรียงข้อมูลแล้ว คุณสามารถใช้เมธอด limit หรือ range ที่อธิบายไว้ด้านล่างเพื่อสร้างการค้นหาที่ซับซ้อนมากขึ้น
จำกัดคำค้นหา
ระบบจะใช้คำค้นหา limitToFirst() และ limitToLast() เพื่อกำหนดจำนวนบุตรหลานสูงสุดที่จะซิงค์สำหรับการเรียกกลับที่ระบุ หากตั้งค่าขีดจำกัดไว้ที่ 100 ในตอนแรกคุณจะได้รับเหตุการณ์ child_added ไม่เกิน 100 รายการเท่านั้น หากคุณมีข้อความที่จัดเก็บไว้ในฐานข้อมูลน้อยกว่า 100 ข้อความ ระบบจะเริ่มเหตุการณ์ child_added สำหรับแต่ละข้อความ อย่างไรก็ตาม หากคุณมีข้อความมากกว่า 100 รายการ คุณจะได้รับเหตุการณ์ child_added สำหรับข้อความเพียง 100 รายการเท่านั้น ซึ่งจะเป็นข้อความที่เรียงลำดับ 100 รายการแรกหากคุณใช้ limitToFirst() หรือข้อความที่เรียงลำดับ 100 รายการสุดท้ายหากคุณใช้ limitToLast() เมื่อรายการเปลี่ยนแปลง คุณจะได้รับเหตุการณ์ child_added สำหรับรายการที่เข้าสู่การค้นหา และเหตุการณ์ child_removed สำหรับรายการที่ออกจากการค้นหา เพื่อให้จำนวนรวมยังคงอยู่ที่ 100
เมื่อใช้ฐานข้อมูลข้อเท็จจริงเกี่ยวกับไดโนเสาร์และ orderByChild() คุณจะค้นหาไดโนเสาร์ที่หนักที่สุด 2 ตัวได้ดังนี้
Java
dinosaursRef.orderByChild("weight").limitToLast(2).addChildEventListener(new ChildEventListener() { @Override public void onChildAdded(DataSnapshot dataSnapshot, String prevChildKey) { System.out.println(dataSnapshot.getKey()); } // ... });
Node.js
const ref = db.ref('dinosaurs'); ref.orderByChild('weight').limitToLast(2).on('child_added', (snapshot) => { console.log(snapshot.key); });
Python
ref = db.reference('dinosaurs') snapshot = ref.order_by_child('weight').limit_to_last(2).get() for key in snapshot: print(key)
Go
ref := client.NewRef("dinosaurs") results, err := ref.OrderByChild("weight").LimitToLast(2).GetOrdered(ctx) if err != nil { log.Fatalln("Error querying database:", err) } for _, r := range results { fmt.Println(r.Key()) }
ระบบจะเรียกใช้ Callback child_added 2 ครั้งพอดี เว้นแต่ว่าจะมีไดโนเสาร์ที่จัดเก็บไว้ในฐานข้อมูลน้อยกว่า 2 ตัว นอกจากนี้ ระบบจะเริ่มทำงานทุกครั้งที่มีการเพิ่มไดโนเสาร์ตัวใหม่ที่มีน้ำหนักมากกว่าลงในฐานข้อมูล
ใน Python การค้นหาจะแสดงผล OrderedDict ที่มีไดโนเสาร์ที่หนักที่สุด 2 ตัวโดยตรง
ในทำนองเดียวกัน คุณสามารถค้นหาไดโนเสาร์ที่เตี้ยที่สุด 2 ตัวได้โดยใช้ limitToFirst() ดังนี้
Java
dinosaursRef.orderByChild("weight").limitToFirst(2).addChildEventListener(new ChildEventListener() { @Override public void onChildAdded(DataSnapshot dataSnapshot, String prevChildKey) { System.out.println(dataSnapshot.getKey()); } // ... });
Node.js
const ref = db.ref('dinosaurs'); ref.orderByChild('height').limitToFirst(2).on('child_added', (snapshot) => { console.log(snapshot.key); });
Python
ref = db.reference('dinosaurs') snapshot = ref.order_by_child('height').limit_to_first(2).get() for key in snapshot: print(key)
Go
ref := client.NewRef("dinosaurs") results, err := ref.OrderByChild("height").LimitToFirst(2).GetOrdered(ctx) if err != nil { log.Fatalln("Error querying database:", err) } for _, r := range results { fmt.Println(r.Key()) }
ระบบจะเรียกใช้ Callback child_added 2 ครั้งพอดี เว้นแต่จะมีไดโนเสาร์ที่จัดเก็บไว้ในฐานข้อมูลน้อยกว่า 2 ตัว นอกจากนี้ ทริกเกอร์จะเริ่มทำงานอีกครั้งหากมีการนำไดโนเสาร์ 2 ตัวแรกออกจากฐานข้อมูล เนื่องจากไดโนเสาร์ตัวใหม่จะกลายเป็นไดโนเสาร์ที่สั้นที่สุดเป็นอันดับ 2 ใน Python การค้นหาจะแสดงผล OrderedDict ที่มีไดโนเสาร์ที่สั้นที่สุดโดยตรง
นอกจากนี้ คุณยังทำการค้นหาแบบจำกัดด้วย orderByValue() ได้ด้วย หากต้องการสร้างลีดเดอร์บอร์ดที่มีไดโนเสาร์ที่ทำคะแนนสูงสุด 3 อันดับแรกในกีฬาไดโนเสาร์ คุณสามารถทำดังนี้
Java
scoresRef.orderByValue().limitToFirst(3).addChildEventListener(new ChildEventListener() { @Override public void onChildAdded(DataSnapshot dataSnapshot, String prevChildKey) { System.out.println("The " + dataSnapshot.getKey() + " score is " + dataSnapshot.getValue()); } // ... });
Node.js
const scoresRef = db.ref('scores'); scoresRef.orderByValue().limitToLast(3).on('value', (snapshot) =>{ snapshot.forEach((data) => { console.log('The ' + data.key + ' dinosaur\'s score is ' + data.val()); }); });
Python
scores_ref = db.reference('scores') snapshot = scores_ref.order_by_value().limit_to_last(3).get() for key, val in snapshot.items(): print(f'The {key} dinosaur\'s score is {val}')
Go
ref := client.NewRef("scores") results, err := ref.OrderByValue().LimitToLast(3).GetOrdered(ctx) if err != nil { log.Fatalln("Error querying database:", err) } for _, r := range results { var score int if err := r.Unmarshal(&score); err != nil { log.Fatalln("Error unmarshaling result:", err) } fmt.Printf("The %s dinosaur's score is %d\n", r.Key(), score) }
คำค้นหาช่วง
การใช้ startAt(), endAt() และ equalTo() ช่วยให้คุณเลือกจุดเริ่มต้นและจุดสิ้นสุดที่กำหนดเองสำหรับคำค้นหาได้ ตัวอย่างเช่น หากต้องการค้นหาไดโนเสาร์ทั้งหมดที่มีความสูงอย่างน้อย 3 เมตร คุณสามารถรวม orderByChild() และ startAt() เข้าด้วยกันได้ ดังนี้
Java
dinosaursRef.orderByChild("height").startAt(3).addChildEventListener(new ChildEventListener() { @Override public void onChildAdded(DataSnapshot dataSnapshot, String prevChildKey) { System.out.println(dataSnapshot.getKey()); } // ... });
Node.js
const ref = db.ref('dinosaurs'); ref.orderByChild('height').startAt(3).on('child_added', (snapshot) => { console.log(snapshot.key); });
Python
ref = db.reference('dinosaurs') snapshot = ref.order_by_child('height').start_at(3).get() for key in snapshot: print(key)
Go
ref := client.NewRef("dinosaurs") results, err := ref.OrderByChild("height").StartAt(3).GetOrdered(ctx) if err != nil { log.Fatalln("Error querying database:", err) } for _, r := range results { fmt.Println(r.Key()) }
คุณสามารถใช้ endAt() เพื่อค้นหาไดโนเสาร์ทั้งหมดที่มีชื่อขึ้นต้นด้วยตัวอักษรที่มาก่อน Pterodactyl ตามลำดับพจนานุกรมได้
Java
dinosaursRef.orderByKey().endAt("pterodactyl").addChildEventListener(new ChildEventListener() { @Override public void onChildAdded(DataSnapshot dataSnapshot, String prevChildKey) { System.out.println(dataSnapshot.getKey()); } // ... });
Node.js
const ref = db.ref('dinosaurs'); ref.orderByKey().endAt('pterodactyl').on('child_added', (snapshot) => { console.log(snapshot.key); });
Python
ref = db.reference('dinosaurs') snapshot = ref.order_by_key().end_at('pterodactyl').get() for key in snapshot: print(key)
Go
ref := client.NewRef("dinosaurs") results, err := ref.OrderByKey().EndAt("pterodactyl").GetOrdered(ctx) if err != nil { log.Fatalln("Error querying database:", err) } for _, r := range results { fmt.Println(r.Key()) }
คุณสามารถรวม startAt() และ endAt() เพื่อจำกัดทั้ง 2 ด้านของคำค้นหาได้ ตัวอย่างต่อไปนี้จะค้นหาไดโนเสาร์ทั้งหมดที่มีชื่อขึ้นต้นด้วยตัวอักษร "b"
Java
dinosaursRef.orderByKey().startAt("b").endAt("b\uf8ff").addChildEventListener(new ChildEventListener() { @Override public void onChildAdded(DataSnapshot dataSnapshot, String prevChildKey) { System.out.println(dataSnapshot.getKey()); } // ... });
Node.js
var ref = db.ref('dinosaurs'); ref.orderByKey().startAt('b').endAt('b\uf8ff').on('child_added', (snapshot) => { console.log(snapshot.key); });
Python
ref = db.reference('dinosaurs') snapshot = ref.order_by_key().start_at('b').end_at('b\uf8ff').get() for key in snapshot: print(key)
Go
ref := client.NewRef("dinosaurs") results, err := ref.OrderByKey().StartAt("b").EndAt("b\uf8ff").GetOrdered(ctx) if err != nil { log.Fatalln("Error querying database:", err) } for _, r := range results { fmt.Println(r.Key()) }
เมธอด equalTo() ช่วยให้คุณกรองตามการทำงานแบบตรงทั้งหมดได้ เช่นเดียวกับกรณีของการค้นหาช่วงอื่นๆ ระบบจะเริ่มทำงานสำหรับโหนดบุตรหลานที่ตรงกันแต่ละโหนด เช่น คุณสามารถใช้การค้นหาต่อไปนี้เพื่อค้นหาไดโนเสาร์ทั้งหมดที่มีความสูง 25 เมตร
Java
dinosaursRef.orderByChild("height").equalTo(25).addChildEventListener(new ChildEventListener() { @Override public void onChildAdded(DataSnapshot dataSnapshot, String prevChildKey) { System.out.println(dataSnapshot.getKey()); } // ... });
Node.js
const ref = db.ref('dinosaurs'); ref.orderByChild('height').equalTo(25).on('child_added', (snapshot) => { console.log(snapshot.key); });
Python
ref = db.reference('dinosaurs') snapshot = ref.order_by_child('height').equal_to(25).get() for key in snapshot: print(key)
Go
ref := client.NewRef("dinosaurs") results, err := ref.OrderByChild("height").EqualTo(25).GetOrdered(ctx) if err != nil { log.Fatalln("Error querying database:", err) } for _, r := range results { fmt.Println(r.Key()) }
การค้นหาช่วงยังมีประโยชน์เมื่อคุณต้องการแบ่งหน้าข้อมูล
สรุปข้อมูลทั้งหมด
คุณสามารถรวมเทคนิคทั้งหมดเหล่านี้เพื่อสร้างการค้นหาที่ซับซ้อนได้ ตัวอย่างเช่น คุณสามารถค้นหาชื่อไดโนเสาร์ที่สั้นกว่าสเตโกซอรัสได้ดังนี้
Java
dinosaursRef.child("stegosaurus").child("height").addValueEventListener(new ValueEventListener() { @Override public void onDataChange(DataSnapshot stegoHeightSnapshot) { Integer favoriteDinoHeight = stegoHeightSnapshot.getValue(Integer.class); Query query = dinosaursRef.orderByChild("height").endAt(favoriteDinoHeight).limitToLast(2); query.addValueEventListener(new ValueEventListener() { @Override public void onDataChange(DataSnapshot dataSnapshot) { // Data is ordered by increasing height, so we want the first entry DataSnapshot firstChild = dataSnapshot.getChildren().iterator().next(); System.out.println("The dinosaur just shorter than the stegosaurus is: " + firstChild.getKey()); } @Override public void onCancelled(DatabaseError databaseError) { // ... } }); } @Override public void onCancelled(DatabaseError databaseError) { // ... } });
Node.js
const ref = db.ref('dinosaurs'); ref.child('stegosaurus').child('height').on('value', (stegosaurusHeightSnapshot) => { const favoriteDinoHeight = stegosaurusHeightSnapshot.val(); const queryRef = ref.orderByChild('height').endAt(favoriteDinoHeight).limitToLast(2); queryRef.on('value', (querySnapshot) => { if (querySnapshot.numChildren() === 2) { // Data is ordered by increasing height, so we want the first entry querySnapshot.forEach((dinoSnapshot) => { console.log('The dinosaur just shorter than the stegasaurus is ' + dinoSnapshot.key); // Returning true means that we will only loop through the forEach() one time return true; }); } else { console.log('The stegosaurus is the shortest dino'); } }); });
Python
ref = db.reference('dinosaurs') favotire_dino_height = ref.child('stegosaurus').child('height').get() query = ref.order_by_child('height').end_at(favotire_dino_height).limit_to_last(2) snapshot = query.get() if len(snapshot) == 2: # Data is ordered by increasing height, so we want the first entry. # Second entry is stegosarus. for key in snapshot: print(f'The dinosaur just shorter than the stegosaurus is {key}') return else: print('The stegosaurus is the shortest dino')
Go
ref := client.NewRef("dinosaurs") var favDinoHeight int if err := ref.Child("stegosaurus").Child("height").Get(ctx, &favDinoHeight); err != nil { log.Fatalln("Error querying database:", err) } query := ref.OrderByChild("height").EndAt(favDinoHeight).LimitToLast(2) results, err := query.GetOrdered(ctx) if err != nil { log.Fatalln("Error querying database:", err) } if len(results) == 2 { // Data is ordered by increasing height, so we want the first entry. // Second entry is stegosarus. fmt.Printf("The dinosaur just shorter than the stegosaurus is %s\n", results[0].Key()) } else { fmt.Println("The stegosaurus is the shortest dino") }
วิธีจัดเรียงข้อมูล
ส่วนนี้จะอธิบายวิธีจัดเรียงข้อมูลเมื่อใช้ฟังก์ชันการจัดเรียงทั้ง 4 ฟังก์ชัน
orderByChild
เมื่อใช้ orderByChild() ข้อมูลที่มีคีย์ย่อยที่ระบุจะเรียงลำดับดังนี้
- รายการย่อยที่มีค่า
nullสำหรับคีย์ย่อยที่ระบุจะแสดงก่อน - ตามด้วยรายการย่อยที่มีค่า
falseสำหรับคีย์ย่อยที่ระบุ หากโหนดบุตรหลานหลายโหนดมีค่าเป็นfalseระบบจะจัดเรียงโหนดเหล่านั้นตามพจนานุกรมตามคีย์ - ตามด้วยรายการย่อยที่มีค่า
trueสำหรับคีย์ย่อยที่ระบุ หากโหนดบุตรหลานหลายโหนดมีค่าเป็นtrueระบบจะจัดเรียงตามพจนานุกรมตามคีย์ - ตามด้วยรายการย่อยที่มีค่าตัวเลข โดยจัดเรียงจากน้อยไปมาก หากโหนดย่อยหลายโหนดมีค่าตัวเลขเดียวกันสำหรับโหนดย่อยที่ระบุ ระบบจะจัดเรียงตามคีย์
- สตริงจะอยู่หลังตัวเลข และจัดเรียงตามพจนานุกรมจากน้อยไปมาก หากโหนดย่อยหลายโหนดมีค่าเดียวกันสำหรับโหนดย่อยที่ระบุ ระบบจะจัดเรียงตามพจนานุกรมตามคีย์
- ออบเจ็กต์จะอยู่ท้ายสุด และจัดเรียงตามพจนานุกรมตามคีย์จากน้อยไปมาก
orderByKey
เมื่อใช้ orderByKey() เพื่อจัดเรียงข้อมูล ระบบจะแสดงข้อมูลตามลำดับจากน้อยไปมากตามคีย์ดังนี้ โปรดทราบว่าคีย์ต้องเป็นสตริงเท่านั้น
- รายการย่อยที่มีคีย์ที่แยกวิเคราะห์เป็นจำนวนเต็ม 32 บิตได้จะแสดงก่อน โดยจัดเรียงตามลำดับจากน้อยไปมาก
- รายการย่อยที่มีค่าสตริงเป็นคีย์จะแสดงถัดไป โดยจัดเรียงตามพจนานุกรมจากน้อยไปมาก
orderByValue
เมื่อใช้ orderByValue() ระบบจะจัดเรียงองค์ประกอบย่อยตามค่าขององค์ประกอบ เกณฑ์การจัดลำดับจะเหมือนกับใน orderByChild() ยกเว้นว่าจะใช้ค่าของโหนดแทนค่าของคีย์ย่อยที่ระบุ