本文檔涵蓋了檢索數據庫數據、數據排序方式以及如何對數據執行簡單查詢的基礎知識。 Admin SDK 中的數據檢索在不同編程語言中的實現方式略有不同。
- 異步偵聽器:通過將異步偵聽器附加到數據庫引用來檢索存儲在 Firebase 實時數據庫中的數據。偵聽器在數據初始狀態時觸發一次,並在數據發生變化時再次觸發。事件偵聽器可以接收多種不同類型的事件。 Java、Node.js 和 Python Admin SDK 支持這種數據檢索模式。
- 阻塞讀取:通過對數據庫引用調用阻塞方法來檢索存儲在 Firebase 實時數據庫中的數據,該方法返回存儲在引用處的數據。每個方法調用都是一次性操作。這意味著SDK不會註冊任何監聽後續數據更新的回調。 Python 和 Go Admin SDK 支持這種數據檢索模型。
入門
讓我們回顧一下上一篇文章中的博客示例,了解如何從 Firebase 數據庫讀取數據。回想一下,示例應用程序中的博客文章存儲在數據庫 URL https://docs-examples.firebaseio.com/server/ saving-data/fireblog/posts.json 中。要讀取您的帖子數據,您可以執行以下操作:
爪哇
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())
去
// 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,每當新數據添加到數據庫引用時都會調用偵聽器函數,並且您不需要編寫任何額外的代碼來實現此目的。
在 Java 和 Node.js 中,回調函數接收DataSnapshot
,它是數據的快照。快照是單個時間點特定數據庫引用的數據圖片。在快照上調用val()
/ getValue()
返回數據的特定於語言的對象表示。如果引用位置不存在數據,則快照的值為null
。 Python 中的get()
方法直接返回數據的 Python 表示形式。 Go 中的Get()
函數將數據解組為給定的數據結構。
請注意,我們在上面的示例中使用了value
事件類型,它會讀取 Firebase 數據庫引用的全部內容,即使只有一項數據發生更改。 value
是下面列出的五種不同事件類型之一,可用於從數據庫讀取數據。
讀取 Java 和 Node.js 中的事件類型
價值
value
事件用於讀取給定數據庫路徑中內容的靜態快照,因為它們在讀取事件時已存在。它會根據初始數據觸發一次,並在每次數據更改時觸發一次。向事件回調傳遞包含該位置的所有數據(包括子數據)的快照。在上面的代碼示例中, value
返回應用程序中的所有博客文章。每次添加新的博客文章時,回調函數都會返回所有帖子。
已添加兒童
child_added
事件通常在從數據庫檢索項目列表時使用。與返回位置的全部內容的value
不同, child_added
對每個現有子項觸發一次,然後每次將新子項添加到指定路徑時再次觸發。事件回調將傳遞包含新子項數據的快照。出於排序目的,還傳遞了第二個參數,其中包含前一個子項的鍵。
如果您只想檢索添加到博客應用程序的每個新帖子的數據,您可以使用child_added
:
爪哇
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
來訪問帖子的author 和title 屬性。您還可以從第二個prevChildKey
參數訪問上一個帖子 ID。
孩子變了
每當子節點被修改時,都會觸發child_changed
事件。這包括對子節點的後代的任何修改。它通常與child_added
和child_removed
結合使用來響應項目列表的更改。傳遞給事件回調的快照包含子項的更新數據。
您可以使用child_changed
在編輯博客文章時讀取更新的數據:
爪哇
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
將有關已刪除帖子的通知記錄到控制台:
爪哇
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 數據庫對事件做出了幾項重要保證:
數據庫事件保證 |
---|
當本地狀態發生變化時,事件總是會被觸發。 |
事件最終始終會反映數據的正確狀態,即使在本地操作或計時導致暫時差異的情況下(例如暫時丟失網絡連接)也是如此。 |
來自單個客戶端的寫入將始終寫入服務器並按順序廣播給其他用戶。 |
值事件始終最後觸發,並保證包含拍攝該快照之前發生的任何其他事件的更新。 |
由於值事件總是最後觸發,因此以下示例始終有效:
爪哇
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); });
分離回調
通過指定事件類型和要刪除的回調函數來刪除回調,如下所示:
爪哇
// Create and attach listener ValueEventListener listener = new ValueEventListener() { // ... }; ref.addValueEventListener(listener); // Remove listener ref.removeEventListener(listener);
Node.js
ref.off('value', originalCallback);
如果您將作用域上下文傳遞給on()
,則在分離回調時必須傳遞它:
爪哇
// Not applicable for Java
Node.js
ref.off('value', originalCallback, ctx);
如果您想刪除某個位置的所有回調,可以執行以下操作:
爪哇
// 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();
讀取數據一次
在某些情況下,調用一次回調然後立即刪除可能很有用。我們創建了一個輔助函數來簡化此操作:
爪哇
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())
去
// 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()
。然後,您可以將它們與其他五個方法結合起來執行複雜的查詢: limitToFirst()
、 limitToLast()
、 startAt()
、 endAt()
和equalTo()
。
由於 Firebase 的所有人都認為恐龍非常酷,因此我們將使用恐龍事實示例數據庫中的片段來演示如何查詢 Firebase 數據庫中的數據。:
{ "lambeosaurus": { "height" : 2.1, "length" : 12.5, "weight": 5000 }, "stegosaurus": { "height" : 4, "length" : 9, "weight" : 2500 } }
您可以通過三種方式對數據進行排序:按子鍵、按鍵或按值。基本的數據庫查詢從這些排序函數之一開始,下面對每個排序函數進行說明。
按指定子鍵排序
您可以通過將該公共子鍵傳遞給orderByChild()
來對節點進行排序。例如,要讀取按高度排序的所有恐龍,您可以執行以下操作:
爪哇
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))
去
// 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 } } }
要立即查詢高度,您可以使用對象的完整路徑而不是單個鍵:
爪哇
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))
去
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) }
查詢一次只能按一個鍵排序。對同一查詢多次調用orderByChild()
會引發錯誤。
按鍵訂購
您還可以使用orderByKey()
方法按節點的鍵對節點進行排序。以下示例按字母順序讀取所有恐龍:
爪哇
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)
去
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 } }
要按恐龍的分數對恐龍進行排序,您可以構建以下查詢:
爪哇
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))
去
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) }
有關使用orderByValue()
時如何對null
、布爾值、字符串和對象值進行排序的說明,請參閱數據的排序方式部分。
複雜查詢
現在已經清楚了數據的排序方式,您可以使用下面描述的限製或範圍方法來構造更複雜的查詢。
限制查詢
limitToFirst()
和limitToLast()
查詢用於設置給定回調要同步的子級的最大數量。如果您將限制設置為 100,則最初最多只會收到 100 個child_added
事件。如果數據庫中存儲的消息少於 100 條,則每條消息都會觸發child_added
事件。但是,如果您的消息超過 100 條,您將僅收到其中 100 條消息的child_added
事件。如果您使用limitToFirst()
則這些是前 100 條有序消息;如果您使用limitToLast()
,則這些是最後 100 條有序消息。當項目發生變化時,您將收到進入查詢的項目的child_added
事件和離開查詢的項目的child_removed
事件,以便總數保持在 100。
使用恐龍事實數據庫和orderByChild()
,您可以找到兩種最重的恐龍:
爪哇
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)
去
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
回調會被觸發兩次,除非數據庫中存儲的恐龍數量少於兩隻。對於添加到數據庫中的每一個新的、更重的恐龍,它也會被觸發。在Python中,查詢直接返回一個包含兩個最重恐龍的OrderedDict
。
同樣,您可以使用limitToFirst()
找到兩個最短的恐龍:
爪哇
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)
去
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
回調會被觸發兩次,除非數據庫中存儲的恐龍數量少於兩隻。如果前兩種恐龍中的一種從數據庫中刪除,它也會再次被觸發,因為新的恐龍現在將是第二短的。在Python中,查詢直接返回包含最短恐龍的OrderedDict
。
您還可以使用orderByValue()
進行限制查詢。如果您想創建一個包含前 3 名得分最高的恐龍運動恐龍的排行榜,您可以執行以下操作:
爪哇
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))
去
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()
允許您為查詢選擇任意起點和終點。例如,如果您想查找所有高度至少三米的恐龍,您可以組合orderByChild()
和startAt()
:
爪哇
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)
去
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()
查找字典順序中名稱位於翼手龍之前的所有恐龍:
爪哇
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)
去
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()
來限制查詢的兩端。以下示例查找名稱以字母“b”開頭的所有恐龍:
爪哇
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)
去
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 米的恐龍:
爪哇
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)
去
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()) }
當您需要對數據進行分頁時,範圍查詢也很有用。
把它們放在一起
您可以結合所有這些技術來創建複雜的查詢。例如,您可以找到僅比劍龍短的恐龍名稱:
爪哇
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')
去
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") }
數據如何排序
本節介紹在使用四個排序函數中的每一個時如何對數據進行排序。
按子訂單
使用orderByChild()
時,包含指定子鍵的數據按如下方式排序:
- 指定子鍵的值為
null
子項排在第一位。 - 接下來是指定子鍵值為
false
的子項。如果多個子項的值為false
,則它們按鍵按字典順序排序。 - 接下來是指定子鍵值為
true
的子項。如果多個子項的值為true
,則它們按鍵按字典順序排序。 - 接下來是具有數值的子項,按升序排序。如果指定子節點的多個子節點具有相同的數值,則它們按鍵排序。
- 字符串位於數字之後,並按字典順序升序排列。如果指定子節點的多個子節點具有相同的值,則它們按鍵按字典順序排序。
- 對象排在最後,並按字典順序按鍵升序排序。
按鍵排序
當使用orderByKey()
對數據進行排序時,數據將按鍵升序返回,如下所示。請記住,鍵只能是字符串。
- 具有可解析為 32 位整數的鍵的子項排在前面,按升序排序。
- 接下來是以字符串值作為鍵的子項,按字典順序升序排列。
按值排序
使用orderByValue()
時,子項按其值排序。排序標準與orderByChild()
中的相同,只是使用節點的值而不是指定子鍵的值。