Join us for Firebase Summit on November 10, 2021. Tune in to learn how Firebase can help you accelerate app development, release with confidence, and scale with ease. Register

檢索數據

本文檔涵蓋了檢索數據庫數據的基礎知識、數據的排序方式以及如何對數據執行簡單查詢。 Admin SDK 中的數據檢索在不同的編程語言中實現略有不同。

  1. 異步聽眾:存儲在火力地堡實時數據庫中的數據由異步聽者附連到參考數據庫中檢索。偵聽器在數據的初始狀態時觸發一次,並在數據發生變化時再次觸發。事件偵聽器可以接收多種不同類型的事件。 Java、Node.js 和 Python Admin SDK 支持這種數據檢索模式。
  2. 阻斷讀取:存儲在火力地堡實時數據庫數據是通過在數據庫引用,返回存儲在基準數據調用阻塞方法檢索。每個方法調用都是一次性操作。這意味著 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());
  }
});
節點.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 。所述get()在Python方法直接返回的數據的一個Python表示。在Get()在Go功能解組數據到一個給定的數據結構。

請注意,我們使用的value在上面的例子中的事件類型,其內容為一個火力地堡數據庫引用的全部內容,即使只有一個數據塊改變。 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) {}
});
節點.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通過檢索值轉換崗位的對象,可以通過調用訪問這篇文章的作者和書名性質authortitle分別。您也可以從第二接入以前的帖子ID prevChildKey說法。

孩子變了

child_changed事件被觸發子節點隨時修改。這包括對子節點後代的任何修改。它通常配合使用child_addedchild_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) {}
});
節點.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_addedchild_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) {}
});
節點.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) {}
});
節點.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);
節點.js
ref.off('value', originalCallback);

如果你通過一個作用域上下文到on()它必須拆卸回調時傳遞:

爪哇
// Not applicable for Java
節點.js
ref.off('value', originalCallback, ctx);

如果要刪除某個位置的所有回調,可以執行以下操作:

爪哇
// No Java equivalent, listeners must be removed individually.
節點.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) {
    // ...
  }
});
節點.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.");
  }

  // ...
});
節點.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) {
    // ...
  }

  // ...
});
節點.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());
  }

  // ...
});
節點.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());
  }

  // ...
});
節點.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)
}

看到數據是如何有序節解釋如何null使用時,布爾型,字符串和對象的值進行排序orderByValue()

複雜查詢

現在,很顯然你的數據是如何排序的,你可以使用限度範圍下述的方法來構建更複雜的查詢。

限制查詢

limitToFirst()limitToLast()查詢用來設定孩子的最大數量,可以同步對於一個給定的回調。如果您設置的100個限度,你將最初只獲得高達100 child_added事件。如果您有存儲在數據庫中少於100的消息,一個child_added事件將觸發對每個消息。但是,如果你有郵件超過100封,你只會收到一個child_added事件的消息100。這是第100有序消息,如果你使用的是limitToFirst()如果您正在使用或過去的100有序消息limitToLast()隨著項目的變化,您會收到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());
  }

  // ...
});
節點.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());
  }

  // ...
});
節點.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());
  }

  // ...
});
節點.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());
  }

  // ...
});
節點.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());
  }

  // ...
});
節點.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());
  }

  // ...
});
節點.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());
  }

  // ...
});
節點.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) {
    // ...
  }
});
節點.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()包含指定的子鍵如下排序的數據:

  1. 與兒童null的指定子鍵值是第一位的。
  2. 用價值孩子false的指定子鍵來下。如果有多個孩子的價值false ,它是按照字典序的關鍵。
  3. 用價值孩子true用於指定的子鍵來下。如果有多個孩子的價值true ,它們是由鍵按字典順序排序。
  4. 接下來是帶有數值的孩子,按升序排序。如果指定子節點的多個子節點具有相同的數值,則它們按關鍵字排序。
  5. 字符串在數字之後,按字典順序升序排列。如果指定子節點的多個子節點具有相同的值,則它們按字典順序按關鍵字排序。
  6. 對象排在最後,並按字典順序按鍵升序排序。

按鍵排序

當使用orderByKey()對數據進行排序,數據在由鍵按升序排列如下返回。請記住,鍵只能是字符串。

  1. 具有可以解析為 32 位整數的鍵的子項首先出現,按升序排序。
  2. 接下來是以字符串值作為鍵的子項,按字典順序升序排列。

按值排序

當使用orderByValue()孩子們通過自己的價值排序。排序標準是一樣orderByChild()除了節點的值被用來代替一個指定的子密鑰的值。