Tài liệu này trình bày các kiến thức cơ bản về cách truy xuất dữ liệu cơ sở dữ liệu, cách sắp xếp dữ liệu và cách thực hiện các truy vấn đơn giản trên dữ liệu. Việc truy xuất dữ liệu trong SDK quản trị được triển khai hơi khác nhau giữa các ngôn ngữ lập trình.
- Trình nghe không đồng bộ: Dữ liệu được lưu trữ trong Firebase Realtime Database được truy xuất bằng cách đính kèm trình nghe không đồng bộ vào tham chiếu cơ sở dữ liệu. Trình nghe được kích hoạt một lần cho trạng thái ban đầu của dữ liệu và lại được kích hoạt bất cứ khi nào dữ liệu thay đổi. Một trình nghe sự kiện có thể nhận được nhiều loại sự kiện. Chế độ truy xuất dữ liệu này được hỗ trợ trong SDK dành cho quản trị viên Java, Node.js và Python.
- Chặn hoạt động đọc: Dữ liệu được lưu trữ trong Firebase Realtime Database được truy xuất bằng cách gọi một phương thức chặn trên tham chiếu cơ sở dữ liệu. Phương thức này sẽ trả về dữ liệu được lưu trữ tại tham chiếu. Mỗi lệnh gọi phương thức là một thao tác một lần. Điều đó có nghĩa là SDK không đăng ký bất kỳ lệnh gọi lại nào để nghe các bản cập nhật dữ liệu tiếp theo. Mô hình truy xuất dữ liệu này được hỗ trợ trong SDK Quản trị viên Python và Go.
Bắt đầu
Hãy xem lại ví dụ về việc viết blog trong bài viết trước để hiểu cách đọc dữ liệu từ cơ sở dữ liệu Firebase. Hãy nhớ rằng các bài đăng trên blog trong ứng dụng mẫu được lưu trữ tại URL cơ sở dữ liệu https://docs-examples.firebaseio.com/server/saving-data/fireblog/posts.json. Để đọc dữ liệu bài đăng, bạn có thể làm như sau:
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())
Tìm
// 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) }
Nếu chạy mã trên, bạn sẽ thấy một đối tượng chứa tất cả bài đăng được ghi nhật ký vào bảng điều khiển. Trong trường hợp của Node.js và Java, hàm trình nghe được gọi bất cứ khi nào dữ liệu mới được thêm vào tham chiếu cơ sở dữ liệu và bạn không cần viết thêm mã nào để thực hiện việc này.
Trong Java và Node.js, hàm gọi lại sẽ nhận được DataSnapshot
, đây là bản tổng quan nhanh về dữ liệu. Ảnh chụp nhanh là hình ảnh của dữ liệu tại một tệp tham chiếu cơ sở dữ liệu cụ thể tại một thời điểm. Việc gọi val()
/ getValue()
trên ảnh chụp nhanh sẽ trả về một đối tượng đại diện cho dữ liệu theo ngôn ngữ cụ thể. Nếu không có dữ liệu nào tại vị trí của tệp đối chiếu, thì giá trị của ảnh chụp nhanh sẽ là null
. Phương thức get()
trong Python trực tiếp trả về một bản trình bày Python của dữ liệu. Hàm Get()
trong Go sẽ chuyển đổi dữ liệu thành một cấu trúc dữ liệu nhất định.
Lưu ý rằng chúng ta đã sử dụng loại sự kiện value
trong ví dụ trên. Loại sự kiện này đọc toàn bộ nội dung của tệp tham chiếu cơ sở dữ liệu Firebase, ngay cả khi chỉ một phần dữ liệu thay đổi. value
là một trong 5 loại sự kiện được liệt kê bên dưới mà bạn có thể dùng để đọc dữ liệu từ cơ sở dữ liệu.
Đọc các loại sự kiện trong Java và Node.js
Giá trị
Sự kiện value
được dùng để đọc ảnh chụp nhanh tĩnh của nội dung tại một đường dẫn cơ sở dữ liệu nhất định, vì nội dung đó tồn tại tại thời điểm xảy ra sự kiện đọc. Phương thức này được kích hoạt một lần với dữ liệu ban đầu và một lần nữa mỗi khi dữ liệu thay đổi. Lệnh gọi lại sự kiện được truyền một ảnh chụp nhanh chứa tất cả dữ liệu tại vị trí đó, bao gồm cả dữ liệu con. Trong ví dụ về mã ở trên, value
trả về tất cả bài đăng trên blog trong ứng dụng của bạn. Mỗi khi thêm một bài đăng trên blog mới, hàm callback sẽ trả về tất cả bài đăng.
Thêm phần tử con
Sự kiện child_added
thường được dùng khi truy xuất danh sách các mục từ cơ sở dữ liệu. Không giống như value
trả về toàn bộ nội dung của vị trí, child_added
được kích hoạt một lần cho mỗi phần tử con hiện có, sau đó được kích hoạt lại mỗi khi một phần tử con mới được thêm vào đường dẫn đã chỉ định. Lệnh gọi lại sự kiện được truyền một ảnh chụp nhanh chứa dữ liệu của phần tử con mới. Để sắp xếp, hàm này cũng được truyền một đối số thứ hai chứa khoá của phần tử con trước đó.
Nếu chỉ muốn truy xuất dữ liệu trên mỗi bài đăng mới được thêm vào ứng dụng viết blog, bạn có thể sử dụng 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); });
Trong ví dụ này, ảnh chụp nhanh sẽ chứa một đối tượng có một bài đăng trên blog riêng lẻ. Vì SDK chuyển đổi bài đăng thành đối tượng bằng cách truy xuất giá trị, nên bạn có quyền truy cập vào các thuộc tính tên tác giả và tiêu đề của bài đăng bằng cách gọi lần lượt author
và title
. Bạn cũng có quyền truy cập vào mã bài đăng trước đó từ đối số prevChildKey
thứ hai.
Trẻ đã thay đổi
Sự kiện child_changed
được kích hoạt bất cứ khi nào một nút con được sửa đổi. Điều này bao gồm mọi nội dung sửa đổi đối với các phần tử con của nút con. Phương thức này thường được dùng kết hợp với child_added
và child_removed
để phản hồi các thay đổi đối với danh sách các mục. Ảnh chụp nhanh được truyền đến lệnh gọi lại sự kiện chứa dữ liệu đã cập nhật cho phần tử con.
Bạn có thể sử dụng child_changed
để đọc dữ liệu đã cập nhật trên các bài đăng trên blog khi chúng được chỉnh sửa:
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); });
Đã xoá thành phần con
Sự kiện child_removed
được kích hoạt khi một phần tử con cấp cao nhất bị xoá. Phương thức này thường được dùng kết hợp với child_added
và child_changed
. Ảnh chụp nhanh được truyền đến lệnh gọi lại sự kiện chứa dữ liệu cho phần tử con đã bị xoá.
Trong ví dụ về blog, bạn có thể sử dụng child_removed
để ghi nhật ký thông báo về bài đăng đã xoá vào bảng điều khiển:
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'); });
Đã di chuyển thành phần con
Sự kiện child_moved
được dùng khi xử lý dữ liệu đã sắp xếp, được đề cập trong phần tiếp theo.
Cam kết về sự kiện
Cơ sở dữ liệu Firebase đưa ra một số đảm bảo quan trọng về sự kiện:
Cam kết về sự kiện cơ sở dữ liệu |
---|
Các sự kiện sẽ luôn được kích hoạt khi trạng thái cục bộ thay đổi. |
Cuối cùng, các sự kiện sẽ luôn phản ánh chính xác trạng thái của dữ liệu, ngay cả trong trường hợp các thao tác cục bộ hoặc thời gian gây ra sự khác biệt tạm thời, chẳng hạn như khi tạm thời mất kết nối mạng. |
Hoạt động ghi từ một ứng dụng khách sẽ luôn được ghi vào máy chủ và truyền đến người dùng khác theo thứ tự. |
Sự kiện giá trị luôn được kích hoạt sau cùng và được đảm bảo chứa thông tin cập nhật từ mọi sự kiện khác đã xảy ra trước khi ảnh chụp nhanh đó được chụp. |
Vì các sự kiện giá trị luôn được kích hoạt sau cùng, nên ví dụ sau đây sẽ luôn hoạt động:
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); });
Tách các lệnh gọi lại
Bạn có thể xoá lệnh gọi lại bằng cách chỉ định loại sự kiện và hàm gọi lại cần xoá, như sau:
Java
// Create and attach listener ValueEventListener listener = new ValueEventListener() { // ... }; ref.addValueEventListener(listener); // Remove listener ref.removeEventListener(listener);
Node.js
ref.off('value', originalCallback);
Nếu bạn đã truyền ngữ cảnh phạm vi vào on()
, thì bạn phải truyền ngữ cảnh đó khi tách lệnh gọi lại:
Java
// Not applicable for Java
Node.js
ref.off('value', originalCallback, ctx);
Nếu muốn xoá tất cả lệnh gọi lại tại một vị trí, bạn có thể làm như sau:
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();
Đọc dữ liệu một lần
Trong một số trường hợp, lệnh gọi lại có thể được gọi một lần rồi xoá ngay. Chúng tôi đã tạo một hàm trợ giúp để giúp bạn thực hiện việc này một cách dễ dàng:
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())
Tìm
// 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) }
Truy vấn dữ liệu
Với các truy vấn cơ sở dữ liệu Firebase, bạn có thể truy xuất dữ liệu một cách có chọn lọc dựa trên nhiều yếu tố. Để tạo truy vấn trong cơ sở dữ liệu, bạn bắt đầu bằng cách chỉ định cách bạn muốn sắp xếp dữ liệu bằng một trong các hàm sắp xếp: orderByChild()
, orderByKey()
hoặc orderByValue()
. Sau đó, bạn có thể kết hợp các phương thức này với 5 phương thức khác để thực hiện các truy vấn phức tạp: limitToFirst()
, limitToLast()
, startAt()
, endAt()
và equalTo()
.
Vì tất cả chúng tôi ở Firebase đều nghĩ rằng khủng long rất thú vị, nên chúng tôi sẽ sử dụng một đoạn mã từ cơ sở dữ liệu mẫu về thông tin về khủng long để minh hoạ cách bạn có thể truy vấn dữ liệu trong cơ sở dữ liệu Firebase.:
{ "lambeosaurus": { "height" : 2.1, "length" : 12.5, "weight": 5000 }, "stegosaurus": { "height" : 4, "length" : 9, "weight" : 2500 } }
Bạn có thể sắp xếp dữ liệu theo 3 cách: theo khoá con, theo khoá hoặc theo giá trị. Một truy vấn cơ sở dữ liệu cơ bản bắt đầu bằng một trong các hàm sắp xếp này, mỗi hàm được giải thích bên dưới.
Sắp xếp theo khoá con đã chỉ định
Bạn có thể sắp xếp các nút theo khoá con chung bằng cách truyền khoá đó đến orderByChild()
. Ví dụ: để đọc tất cả các loài khủng long được sắp xếp theo chiều cao, bạn có thể làm như sau:
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))
Tìm
// 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) }
Mọi nút không có khoá con mà chúng ta đang truy vấn sẽ được sắp xếp theo giá trị null
, nghĩa là nút đó sẽ đứng đầu trong thứ tự. Để biết thông tin chi tiết về cách sắp xếp dữ liệu, hãy xem phần Cách sắp xếp dữ liệu.
Bạn cũng có thể sắp xếp các truy vấn theo phần tử con lồng ghép sâu, thay vì chỉ phần tử con ở cấp dưới. Điều này sẽ hữu ích nếu bạn có dữ liệu lồng sâu như sau:
{ "lambeosaurus": { "dimensions": { "height" : 2.1, "length" : 12.5, "weight": 5000 } }, "stegosaurus": { "dimensions": { "height" : 4, "length" : 9, "weight" : 2500 } } }
Để truy vấn chiều cao hiện tại, bạn có thể sử dụng đường dẫn đầy đủ đến đối tượng thay vì một khoá duy nhất:
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))
Tìm
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) }
Mỗi lần, truy vấn chỉ có thể sắp xếp theo một khoá. Việc gọi orderByChild()
nhiều lần trên cùng một truy vấn sẽ gây ra lỗi.
Sắp xếp theo khoá
Bạn cũng có thể sắp xếp các nút theo khoá của chúng bằng phương thức orderByKey()
. Ví dụ sau đây đọc tất cả các loài khủng long theo thứ tự bảng chữ cái:
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)
Tìm
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)
Sắp xếp theo giá trị
Bạn có thể sắp xếp các nút theo giá trị của khoá con bằng phương thức orderByValue()
. Giả sử các loài khủng long đang tổ chức một cuộc thi thể thao và bạn đang theo dõi điểm số của chúng theo định dạng sau:
{ "scores": { "bruhathkayosaurus" : 55, "lambeosaurus" : 21, "linhenykus" : 80, "pterodactyl" : 93, "stegosaurus" : 5, "triceratops" : 22 } }
Để sắp xếp các loài khủng long theo điểm số, bạn có thể tạo truy vấn sau:
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))
Tìm
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) }
Hãy xem phần Cách sắp xếp dữ liệu để biết nội dung giải thích về cách sắp xếp các giá trị null
, boolean, chuỗi và đối tượng khi sử dụng orderByValue()
.
Truy vấn phức tạp
Giờ đây, khi đã rõ cách sắp xếp dữ liệu, bạn có thể sử dụng các phương thức giới hạn hoặc dải ô được mô tả bên dưới để tạo các truy vấn phức tạp hơn.
Giới hạn truy vấn
Truy vấn limitToFirst()
và limitToLast()
được dùng để đặt số lượng tối đa phần tử con cần đồng bộ hoá cho một lệnh gọi lại nhất định. Nếu đặt giới hạn là 100, ban đầu bạn sẽ chỉ nhận được tối đa 100 sự kiện child_added
. Nếu bạn có dưới 100 tin nhắn được lưu trữ trong cơ sở dữ liệu, thì một sự kiện child_added
sẽ kích hoạt cho mỗi tin nhắn. Tuy nhiên, nếu có hơn 100 thư, bạn sẽ chỉ nhận được sự kiện child_added
cho 100 thư trong số đó. Đây là 100 tin nhắn được sắp xếp đầu tiên nếu bạn đang sử dụng limitToFirst()
hoặc 100 tin nhắn được sắp xếp gần đây nhất nếu bạn đang sử dụng limitToLast()
. Khi các mục thay đổi, bạn sẽ nhận được sự kiện child_added
cho các mục nhập vào truy vấn và sự kiện child_removed
cho các mục rời khỏi truy vấn, để tổng số mục vẫn giữ nguyên ở mức 100.
Bằng cách sử dụng cơ sở dữ liệu thông tin về khủng long và orderByChild()
, bạn có thể tìm thấy hai loài khủng long nặng nhất:
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)
Tìm
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()) }
Lệnh gọi lại child_added
được kích hoạt đúng hai lần, trừ khi có ít hơn hai con khủng long được lưu trữ trong cơ sở dữ liệu. Hàm này cũng sẽ được kích hoạt cho mọi loài khủng long mới, nặng hơn được thêm vào cơ sở dữ liệu.
Trong Python, truy vấn này trực tiếp trả về một OrderedDict
chứa hai loài khủng long nặng nhất.
Tương tự, bạn có thể tìm thấy hai loài khủng long ngắn nhất bằng cách sử dụng 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)
Tìm
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()) }
Lệnh gọi lại child_added
được kích hoạt đúng hai lần, trừ phi có ít hơn hai con khủng long được lưu trữ trong cơ sở dữ liệu. Hàm này cũng sẽ được kích hoạt lại nếu một trong hai loài khủng long đầu tiên bị xoá khỏi cơ sở dữ liệu, vì một loài khủng long mới hiện sẽ là loài ngắn thứ hai. Trong Python, truy vấn này trực tiếp trả về một OrderedDict
chứa những loài khủng long ngắn nhất.
Bạn cũng có thể thực hiện các truy vấn giới hạn bằng orderByValue()
. Nếu muốn tạo bảng xếp hạng gồm 3 con khủng long thể thao có điểm số cao nhất, bạn có thể làm như sau:
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))
Tìm
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) }
Truy vấn phạm vi
Việc sử dụng startAt()
, endAt()
và equalTo()
cho phép bạn chọn điểm bắt đầu và điểm kết thúc tuỳ ý cho truy vấn. Ví dụ: nếu muốn tìm tất cả các loài khủng long cao ít nhất 3 mét, bạn có thể kết hợp orderByChild()
và 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)
Tìm
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()) }
Bạn có thể sử dụng endAt()
để tìm tất cả các loài khủng long có tên đứng trước Pterodactyl theo thứ tự bảng chữ cái:
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)
Tìm
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()) }
Bạn có thể kết hợp startAt()
và endAt()
để giới hạn cả hai đầu truy vấn. Ví dụ sau đây tìm tất cả các loài khủng long có tên bắt đầu bằng chữ cái "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)
Tìm
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()) }
Phương thức equalTo()
cho phép bạn lọc dựa trên kết quả khớp chính xác. Giống như các truy vấn phạm vi khác, truy vấn này sẽ kích hoạt cho mỗi nút con trùng khớp. Ví dụ: bạn có thể sử dụng truy vấn sau để tìm tất cả khủng long cao 25 mét:
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)
Tìm
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()) }
Truy vấn theo phạm vi cũng hữu ích khi bạn cần phân trang dữ liệu.
Đang kết hợp
Bạn có thể kết hợp tất cả các kỹ thuật này để tạo truy vấn phức tạp. Ví dụ: bạn có thể tìm thấy tên của loài khủng long ngắn hơn Stegosaurus:
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')
Tìm
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") }
Cách sắp xếp dữ liệu
Phần này giải thích cách sắp xếp dữ liệu khi sử dụng từng hàm sắp xếp trong số 4 hàm.
orderByChild
Khi sử dụng orderByChild()
, dữ liệu chứa khoá con đã chỉ định sẽ được sắp xếp như sau:
- Các phần tử con có giá trị
null
cho khoá con được chỉ định sẽ xuất hiện trước tiên. - Tiếp theo là các phần tử con có giá trị
false
cho khoá con đã chỉ định. Nếu nhiều phần tử con có giá trị làfalse
, thì các phần tử con đó sẽ được sắp xếp theo thứ tự bảng chữ cái theo khoá. - Tiếp theo là các phần tử con có giá trị
true
cho khoá con đã chỉ định. Nếu nhiều phần tử con có giá trị làtrue
, thì các phần tử con đó sẽ được sắp xếp theo thứ tự bảng chữ cái theo khoá. - Tiếp theo là các phần tử con có giá trị số, được sắp xếp theo thứ tự tăng dần. Nếu nhiều nút con có cùng một giá trị số cho nút con được chỉ định, thì các nút con đó sẽ được sắp xếp theo khoá.
- Chuỗi đứng sau số và được sắp xếp theo thứ tự bảng chữ cái tăng dần. Nếu nhiều nút con có cùng một giá trị cho nút con được chỉ định, thì các nút con đó sẽ được sắp xếp theo thứ tự bảng chữ cái theo khoá.
- Các đối tượng xuất hiện cuối cùng và được sắp xếp theo thứ tự tăng dần theo thứ tự bảng chữ cái theo khoá.
orderByKey
Khi sử dụng orderByKey()
để sắp xếp dữ liệu, dữ liệu sẽ được trả về theo thứ tự tăng dần theo khoá như sau. Xin lưu ý rằng khoá chỉ có thể là chuỗi.
- Các phần tử con có khoá có thể được phân tích cú pháp dưới dạng số nguyên 32 bit sẽ xuất hiện trước tiên, được sắp xếp theo thứ tự tăng dần.
- Tiếp theo là các phần tử con có giá trị chuỗi làm khoá, được sắp xếp theo thứ tự bảng chữ cái theo thứ tự tăng dần.
orderByValue
Khi sử dụng orderByValue()
, các phần tử con được sắp xếp theo giá trị của chúng. Tiêu chí sắp xếp giống như trong orderByChild()
, ngoại trừ việc giá trị của nút được sử dụng thay vì giá trị của khoá con được chỉ định.