Эта страница была переведа с помощью Cloud Translation API.
Switch to English

Извлечение данных

Этот документ охватывает основы извлечения данных из базы данных, порядок упорядочения данных и способы выполнения простых запросов к данным. Поиск данных в Admin SDK реализован несколько по-разному на разных языках программирования.

  1. Асинхронные прослушиватели: данные, хранящиеся в базе данных Firebase Realtime, извлекаются путем присоединения асинхронного прослушивателя к ссылке на базу данных. Слушатель запускается один раз для начального состояния данных и снова каждый раз, когда данные изменяются. Слушатель события может получить несколько различных типов событий . Этот режим извлечения данных поддерживается в Java, Node.js и Python Admin SDK.
  2. Блокировка чтения: данные, хранящиеся в базе данных Firebase Realtime, извлекаются путем вызова метода блокировки ссылки на базу данных, которая возвращает данные, хранящиеся в ссылке. Каждый вызов метода является одноразовой операцией. Это означает, что 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
// Import Admin SDK
var admin = require("firebase-admin");

// Get a database reference to our posts
var db = admin.database();
var ref = db.ref("server/saving-data/fireblog/posts");

// Attach an asynchronous callback to read the data at our posts reference
ref.on("value", function(snapshot) {
  console.log(snapshot.val());
}, function (errorObject) {
  console.log("The read failed: " + errorObject.code);
});
питон
# 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 в приведенном выше примере, который считывает все содержимое ссылки на базу данных Firebase, даже если изменился только один фрагмент данных. value - это один из пяти различных типов событий, перечисленных ниже, которые вы можете использовать для чтения данных из базы данных.

Читать типы событий в Java и Node.js

Стоимость

Событие value используется для чтения статического снимка содержимого по заданному пути к базе данных, поскольку они существовали на момент события read. Он запускается один раз с начальными данными и снова каждый раз, когда данные изменяются. Обратному вызову события передается снимок, содержащий все данные в этом месте, включая дочерние данные. В приведенном выше примере кода value возвращаются все сообщения блога в вашем приложении. Каждый раз, когда добавляется новое сообщение в блоге, функция обратного вызова возвращает все сообщения.

Ребенок добавлен

Событие child_added обычно используется при получении списка элементов из базы данных. В отличие от value которое возвращает все содержимое местоположения, child_added запускается один раз для каждого существующего дочернего 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", function(snapshot, prevChildKey) {
  var 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 .

Ребенок изменился

Событие 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", function(snapshot) {
  var 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
var ref = db.ref("server/saving-data/fireblog/posts");

// Get the data on a post that has been removed
ref.on("child_removed", function(snapshot) {
  var deletedPost = snapshot.val();
  console.log("The blog post titled '" + deletedPost.title + "' has been deleted");
});

Ребенок переехал

Событие child_moved используется при работе с упорядоченными данными, о чем 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
var count = 0;

ref.on("child_added", function(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", function(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, this);

Если вы хотите удалить все обратные вызовы в определенном месте, вы можете сделать следующее:

Ява
// 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", function(data) {
  // do some stuff once
});
питон
# 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
var db = firebaseAdmin.database();
var ref = db.ref("dinosaurs");
ref.orderByChild("height").on("child_added", function(snapshot) {
  console.log(snapshot.key + " was " + snapshot.val().height + " meters tall");
});
питон
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
var ref = db.ref("dinosaurs");
ref.orderByChild("dimensions/height").on("child_added", function(snapshot) {
  console.log(snapshot.key + " was " + snapshot.val().height + " meters tall");
});
питон
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() вызов 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", function(snapshot) {
  console.log(snapshot.key);
});
питон
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
var scoresRef = db.ref("scores");
scoresRef.orderByValue().on("value", function(snapshot) {
  snapshot.forEach(function(data) {
    console.log("The " + data.key + " dinosaur's score is " + data.val());
  });
});
питон
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 событие child_added . Однако, если у вас более 100 сообщений, вы получите событие child_added для 100 из этих сообщений. Это первые 100 упорядоченных сообщений, если вы используете limitToFirst() или последние 100 упорядоченных сообщений, если вы используете limitToLast() . При изменении элементов вы будете получать события child_added для элементов, которые вводят запрос, и события child_removed для элементов, которые его покидают, так что общее число остается 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
var ref = db.ref("dinosaurs");
ref.orderByChild("weight").limitToLast(2).on("child_added", function(snapshot) {
  console.log(snapshot.key);
});
питон
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 вызов 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
var ref = db.ref("dinosaurs");
ref.orderByChild("height").limitToFirst(2).on("child_added", function(snapshot) {
  console.log(snapshot.key);
});
питон
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 вызов 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
var scoresRef = db.ref("scores");
scoresRef.orderByValue().limitToLast(3).on("value", function(snapshot) {
  snapshot.forEach(function(data) {
    console.log("The " + data.key + " dinosaur's score is " + data.val());
  });
});
питон
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
  var ref = db.ref("dinosaurs");
  ref.orderByChild("height").startAt(3).on("child_added", function(snapshot) {
    console.log(snapshot.key);
  });
питон
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
var ref = db.ref("dinosaurs");
ref.orderByKey().endAt("pterodactyl").on("child_added", function(snapshot) {
  console.log(snapshot.key);
});
питон
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", function(snapshot) {
  console.log(snapshot.key);
});
питон
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
var ref = db.ref("dinosaurs");
ref.orderByChild("height").equalTo(25).on("child_added", function(snapshot) {
  console.log(snapshot.key);
});
питон
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
var ref = db.ref("dinosaurs");
ref.child("stegosaurus").child("height").on("value", function(stegosaurusHeightSnapshot) {
  var favoriteDinoHeight = stegosaurusHeightSnapshot.val();

  var queryRef = ref.orderByChild("height").endAt(favoriteDinoHeight).limitToLast(2)
  queryRef.on("value", function(querySnapshot) {
    if (querySnapshot.numChildren() === 2) {
      // Data is ordered by increasing height, so we want the first entry
      querySnapshot.forEach(function(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");
    }
  });
});
питон
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

При использовании orderByChild() данные, содержащие указанный дочерний ключ, упорядочиваются следующим образом:

  1. Дети с null значением для указанного дочернего ключа идут первыми.
  2. Далее идут потомки со значением false для указанного дочернего ключа. Если у нескольких детей значение false , они сортируются лексикографически по ключу.
  3. Далее идут потомки со значением true для указанного дочернего ключа. Если у нескольких детей значение true , они сортируются лексикографически по ключу.
  4. Далее идут дети с числовым значением, отсортированные по возрастанию. Если несколько дочерних узлов имеют одинаковое числовое значение для указанного дочернего узла, они сортируются по ключу.
  5. Строки идут после чисел и сортируются лексикографически в порядке возрастания. Если несколько дочерних узлов имеют одинаковое значение для указанного дочернего узла, они упорядочиваются по лексикографическому признаку
  6. Объекты идут последними и сортируются лексикографически по ключам в порядке возрастания.

orderByKey

При использовании orderByKey() для сортировки данных данные возвращаются в порядке возрастания по ключу следующим образом. Имейте в виду, что ключи могут быть только строками.

  1. Сначала идут дочерние элементы с ключом, который может быть проанализирован как 32-разрядное целое число, отсортированные по возрастанию.
  2. Далее следуют дети со строковым значением в качестве ключа, отсортированные лексикографически в порядке возрастания.

orderByValue

При использовании orderByValue() дочерние элементы упорядочены по их значению. Критерии упорядочения такие же, как в orderByChild() , за исключением того, что значение узла используется вместо значения указанного дочернего ключа.