กำลังดึงข้อมูล

เอกสารนี้ครอบคลุมพื้นฐานการดึงข้อมูลฐานข้อมูล วิธีเรียงลำดับข้อมูล และวิธีการค้นหาข้อมูลอย่างง่ายๆ การดึงข้อมูลใน Admin SDK มีการใช้งานในภาษาโปรแกรมต่างๆ แตกต่างกันเล็กน้อย

  1. Listener แบบอะซิงโครนัส: ระบบจะดึงข้อมูลที่จัดเก็บไว้ในฐานข้อมูลเรียลไทม์ของ Firebase โดยการแนบ Listener แบบไม่พร้อมกันกับการอ้างอิงฐานข้อมูล ระบบจะทริกเกอร์ Listener 1 ครั้งสำหรับสถานะเริ่มต้นของข้อมูล และอีกครั้งเมื่อข้อมูลเปลี่ยนแปลง Listener เหตุการณ์อาจได้รับเหตุการณ์ประเภทต่างๆ มากมาย โหมดการดึงข้อมูลนี้รองรับใน Java, Node.js และ Python Admin SDK
  2. การบล็อกการอ่าน: ระบบจะดึงข้อมูลที่จัดเก็บไว้ในฐานข้อมูลเรียลไทม์ของ Firebase ด้วยการเรียกใช้วิธีการบล็อกในการอ้างอิงฐานข้อมูล ซึ่งจะแสดงผลข้อมูลที่เก็บไว้ในข้อมูลอ้างอิง การเรียกใช้เมธอดแต่ละรายการจะเป็นการดำเนินการแบบครั้งเดียว ซึ่งหมายความว่า SDK จะไม่บันทึกโค้ดเรียกกลับที่คอยฟังการอัปเดตข้อมูลที่ตามมา การดึงข้อมูลโมเดลนี้รองรับใน Python และ Go Admin SDK

เริ่มต้นใช้งาน

มาดูตัวอย่างการเขียนบล็อกจากบทความก่อนหน้านี้เพื่อทำความเข้าใจวิธีอ่านข้อมูลจากฐานข้อมูล 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 ฟังก์ชันเรียกกลับจะได้รับ DataSnapshot ซึ่งเป็นภาพรวมของข้อมูล สแนปชอตคือรูปภาพของข้อมูลในการอ้างอิงฐานข้อมูลหนึ่งๆ ณ เวลาใดเวลาหนึ่ง การเรียก val() / getValue() ในสแนปชอตจะแสดงผลการแทนออบเจ็กต์ที่เจาะจงภาษาของข้อมูล หากไม่มีข้อมูลอยู่ที่ตำแหน่งของข้อมูลอ้างอิง ค่าของสแนปชอตจะเป็น null เมธอด get() ใน Python จะแสดงผลการแทนค่า Python ของข้อมูลโดยตรง ฟังก์ชัน Get() ใน Go จะยกเลิกมาร์ชข้อมูลลงในโครงสร้างข้อมูลที่กำหนด

โปรดทราบว่า เราใช้ประเภทเหตุการณ์ value ในตัวอย่างด้านบน ซึ่งจะอ่านเนื้อหาทั้งหมดของการอ้างอิงฐานข้อมูล Firebase แม้ว่าจะมีการเปลี่ยนแปลงข้อมูลเพียงส่วนเดียวก็ตาม value เป็น 1 ใน 5 ประเภทเหตุการณ์ที่แตกต่างกันซึ่งระบุไว้ด้านล่างที่คุณใช้เพื่ออ่านข้อมูลจากฐานข้อมูลได้

อ่านประเภทเหตุการณ์ใน Java และ Node.js

ค่า

ระบบใช้เหตุการณ์ value เพื่ออ่านสแนปชอตแบบคงที่ของเนื้อหาในเส้นทางฐานข้อมูลที่ระบุ ตามที่มีอยู่ในขณะที่เกิดเหตุการณ์การอ่าน ระบบจะทริกเกอร์ 1 ครั้งโดยใช้ข้อมูลเริ่มต้นและอีกครั้งทุกครั้งที่ข้อมูลมีการเปลี่ยนแปลง โค้ดเรียกกลับของเหตุการณ์จะส่งผ่านสแนปชอตที่มีข้อมูลทั้งหมดที่ตำแหน่งนั้น รวมถึงข้อมูลย่อย ในตัวอย่างโค้ดข้างต้น value แสดงบล็อกโพสต์ทั้งหมดในแอปของคุณ ทุกครั้งที่มีการเพิ่มบล็อกโพสต์ใหม่ ฟังก์ชันเรียกกลับจะแสดงผลโพสต์ทั้งหมด

เพิ่มผู้เผยแพร่โฆษณาย่อยแล้ว

โดยปกติแล้ว เหตุการณ์ child_added จะใช้เมื่อดึงรายการจากฐานข้อมูล ซึ่งต่างจาก value ที่แสดงผลเนื้อหาทั้งหมดของตำแหน่งดังกล่าว child_added จะถูกทริกเกอร์ 1 ครั้งสำหรับหน่วยย่อยที่มีอยู่แต่ละราย และทริกเกอร์อีกครั้งทุกครั้งที่มีการเพิ่มตำแหน่งย่อยใหม่ลงในเส้นทางที่ระบุ โค้ดเรียกกลับของเหตุการณ์จะได้รับการส่งผ่านสแนปชอตที่มีข้อมูลของบุตรหลานใหม่ สำหรับวัตถุประสงค์ในการจัดลำดับ ระบบจะส่งอาร์กิวเมนต์ที่ 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 เพื่อตอบสนองต่อการเปลี่ยนแปลงของรายการ สแนปชอตที่ส่งไปยังโค้ดเรียกกลับของเหตุการณ์จะมีข้อมูลที่อัปเดตสำหรับบัญชีย่อย

คุณจะใช้ 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 สแนปชอตที่ส่งไปยังโค้ดเรียกกลับของเหตุการณ์จะมีข้อมูลสำหรับบัญชีย่อยที่ถูกนำออก

ในตัวอย่างบล็อก คุณสามารถใช้ 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);
});

การปลดโค้ดเรียกกลับออก

ระบบจะนำการเรียกกลับออกโดยการระบุประเภทเหตุการณ์และฟังก์ชันเรียกกลับที่จะนำออก ดังตัวอย่างต่อไปนี้

Java
// Create and attach listener
ValueEventListener listener = new ValueEventListener() {
    // ...
};
ref.addValueEventListener(listener);

// Remove listener
ref.removeEventListener(listener);
Node.js
ref.off('value', originalCallback);

หากคุณส่งบริบทของขอบเขตไปยัง on() คุณต้องส่งบริบทดังกล่าวเมื่อปลดโค้ดเรียกกลับออก

Java
// Not applicable for Java
Node.js
ref.off('value', originalCallback, ctx);

หากต้องการนำโค้ดเรียกกลับทั้งหมดออกจากสถานที่ตั้ง คุณจะดำเนินการต่อไปนี้ได้

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();

การอ่านข้อมูล 1 ครั้ง

ในบางกรณี การเรียกกลับจะเรียกเพียงครั้งเดียวแล้วลบออกทันทีอาจมีประโยชน์ เราได้สร้างฟังก์ชันตัวช่วยเพื่อช่วยให้คุณดำเนินการต่อไปนี้ได้ง่ายขึ้น

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('{0} was {1} meters tall'.format(key, val))
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 ซึ่งหมายความว่าโหนดจะอยู่ในลำดับแรก ดูรายละเอียดเกี่ยวกับวิธีเรียงลำดับข้อมูลได้ที่ส่วนวิธีเรียงลำดับข้อมูล

นอกจากนี้ การค้นหายังอาจเรียงลำดับตามรายการย่อยที่อยู่ซ้อนกันอยู่ แทนที่จะจัดเรียงเฉพาะรายการย่อยลงมา 1 ระดับ วิธีนี้มีประโยชน์หากคุณมีข้อมูลที่ฝังไว้ลึกเช่นนี้

{
  "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('{0} was {1} meters tall'.format(key, val))
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('The {0} dinosaur\'s score is {1}'.format(key, 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()

การค้นหาที่ซับซ้อน

เมื่อเข้าใจการเรียงลำดับข้อมูลอย่างชัดเจนแล้ว คุณสามารถใช้เมธอดขีดจำกัดหรือช่วงที่อธิบายไว้ด้านล่าง เพื่อสร้างคำค้นหาที่ซับซ้อนยิ่งขึ้น

จำกัดการค้นหา

คำค้นหา 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())
}

โค้ดเรียกกลับ 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())
}

โค้ดเรียกกลับ child_added จะทริกเกอร์ 2 ครั้งพอดี ยกเว้นว่ามีไดโนเสาร์น้อยกว่า 2 ตัวที่จัดเก็บไว้ในฐานข้อมูล และจะถูกยิงอีกครั้งหากมีการนำไดโนเสาร์ 1 ใน 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('The {0} dinosaur\'s score is {1}'.format(key, 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(u'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('The dinosaur just shorter than the stegosaurus is {0}'.format(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() ข้อมูลที่มีคีย์ย่อยที่ระบุจะเรียงลำดับดังนี้

  1. ผู้เผยแพร่โฆษณาย่อยที่มีค่า null สำหรับคีย์ย่อยที่ระบุมาก่อน
  2. รายการย่อยที่มีค่า false สำหรับคีย์ย่อยที่ระบุจะอยู่ในลำดับถัดไป หากเด็กหลายคนมีค่าเป็น false ระบบจะจัดเรียงแบบพจนานุกรมตามคีย์
  3. รายการย่อยที่มีค่า true สำหรับคีย์ย่อยที่ระบุจะอยู่ในลำดับถัดไป หากเด็กหลายคนมีค่าเป็น true ระบบจะจัดเรียงแบบพจนานุกรมตามคีย์
  4. เนื้อหาย่อยที่มีค่าตัวเลขจะเรียงตามลำดับจากน้อยไปมาก หากโหนดย่อยหลายรายการมีค่าตัวเลขเดียวกันสำหรับโหนดย่อยที่ระบุ ระบบจะจัดเรียงตามคีย์
  5. สตริงจะอยู่หลังตัวเลขและจัดเรียงแบบพจนานุกรมตามลำดับจากน้อยไปมาก หากรายการย่อยหลายคนมีค่าเดียวกันสำหรับโหนดย่อยที่ระบุ โหนดย่อยเหล่านั้นจะเรียงลำดับแบบพจนานุกรมตามคีย์
  6. วัตถุจะมาเป็นอันดับสุดท้ายและจัดเรียงแบบพจนานุกรมตามคีย์ในลำดับจากน้อยไปมาก

คีย์การสั่งซื้อ

เมื่อใช้ orderByKey() เพื่อจัดเรียงข้อมูล ระบบจะแสดงผลข้อมูลตามลำดับจากน้อยไปมากตามคีย์ดังนี้ โปรดทราบว่าคีย์ต้องเป็นสตริงเท่านั้น

  1. ผู้เผยแพร่โฆษณาย่อยที่มีคีย์ซึ่งแยกวิเคราะห์เป็นจำนวนเต็ม 32 บิตได้ก่อน โดยเรียงลำดับจากน้อยไปหามาก
  2. รายการย่อยที่มีค่าสตริงเป็นคีย์ถัดไป โดยจัดเรียงแบบพจนานุกรมจากน้อยไปหามาก

มูลค่าการสั่งซื้อ

เมื่อใช้ orderByValue() เด็กจะเรียงลำดับตามมูลค่า เกณฑ์การจัดลำดับจะเหมือนกับใน orderByChild() แต่ต่างกันตรงที่จะมีการใช้ค่าของโหนดแทนค่าของคีย์ย่อยที่ระบุ