Работа со списками данных в Интернете

Получить ссылку на базу данных

Чтобы читать или записывать данные из базы данных, вам понадобится экземпляр firebase.database.Reference :

Web modular API

import { getDatabase } from "firebase/database";

const database = getDatabase();

Web namespaced API

var database = firebase.database();

Чтение и написание списков

Добавить в список данных

Используйте метод push() для добавления данных в список в многопользовательских приложениях. Метод push() генерирует уникальный ключ каждый раз, когда к указанной ссылке Firebase добавляется новый дочерний элемент. Используя эти автоматически сгенерированные ключи для каждого нового элемента в списке, несколько клиентов могут одновременно добавлять дочерние элементы в одно и то же место без конфликтов записи. Уникальный ключ, сгенерированный функцией push() основан на временной метке, поэтому элементы списка автоматически упорядочиваются в хронологическом порядке.

Вы можете использовать ссылку на новые данные, возвращаемые методом push() , чтобы получить значение автоматически сгенерированного ключа дочернего элемента или установить данные для дочернего элемента. Свойство .key ссылки push() содержит автоматически сгенерированный ключ.

Вы можете использовать эти автоматически сгенерированные ключи, чтобы упростить выравнивание структуры данных. Дополнительные сведения см. в примере разветвления данных.

Например, push() можно использовать для добавления новой публикации в список публикаций в социальном приложении:

Web modular API

import { getDatabase, ref, push, set } from "firebase/database";

// Create a new post reference with an auto-generated id
const db = getDatabase();
const postListRef = ref(db, 'posts');
const newPostRef = push(postListRef);
set(newPostRef, {
    // ...
});

Web namespaced API

// Create a new post reference with an auto-generated id
var postListRef = firebase.database().ref('posts');
var newPostRef = postListRef.push();
newPostRef.set({
    // ...
});

Прослушивание дочерних событий

Дочерние события запускаются в ответ на определенные операции, которые происходят с дочерними узлами узла в результате такой операции, как добавление нового дочернего узла с помощью метода push() или обновление дочернего элемента с помощью метода update() .

Событие Типичное использование
child_added Получайте списки элементов или прослушивайте дополнения к списку элементов. Это событие запускается один раз для каждого существующего дочернего элемента, а затем снова каждый раз, когда к указанному пути добавляется новый дочерний элемент. Слушателю передается снимок, содержащий данные нового дочернего элемента.
child_changed Прослушивайте изменения элементов в списке. Это событие срабатывает каждый раз, когда изменяется дочерний узел. Сюда входят любые изменения потомков дочернего узла. Снимок, передаваемый прослушивателю событий, содержит обновленные данные для дочернего процесса.
child_removed Прослушивайте удаление элементов из списка. Это событие срабатывает при удалении непосредственного дочернего элемента. Снимок, передаваемый в блок обратного вызова, содержит данные об удаленном дочернем элементе.
child_moved Прослушивайте изменения порядка элементов в упорядоченном списке. События child_moved всегда следуют за событием child_changed , которое вызвало изменение порядка элемента (на основе текущего метода order-by).

Каждый из них вместе может быть полезен для прослушивания изменений определенного узла в базе данных. Например, приложение для социальных блогов может использовать эти методы вместе для отслеживания активности в комментариях к публикации, как показано ниже:

Web modular API

import { getDatabase, ref, onChildAdded, onChildChanged, onChildRemoved } from "firebase/database";

const db = getDatabase();
const commentsRef = ref(db, 'post-comments/' + postId);
onChildAdded(commentsRef, (data) => {
  addCommentElement(postElement, data.key, data.val().text, data.val().author);
});

onChildChanged(commentsRef, (data) => {
  setCommentValues(postElement, data.key, data.val().text, data.val().author);
});

onChildRemoved(commentsRef, (data) => {
  deleteComment(postElement, data.key);
});

Web namespaced API

var commentsRef = firebase.database().ref('post-comments/' + postId);
commentsRef.on('child_added', (data) => {
  addCommentElement(postElement, data.key, data.val().text, data.val().author);
});

commentsRef.on('child_changed', (data) => {
  setCommentValues(postElement, data.key, data.val().text, data.val().author);
});

commentsRef.on('child_removed', (data) => {
  deleteComment(postElement, data.key);
});

Слушайте события значений

Хотя прослушивание дочерних событий является рекомендуемым способом чтения списков данных, в некоторых ситуациях полезно прослушивать события значений в ссылке на список.

Присоединение наблюдателя value к списку данных вернет весь список данных в виде единого снимка, который затем можно будет перебрать для доступа к отдельным дочерним элементам.

Даже если запросу соответствует только одно совпадение, снимок по-прежнему представляет собой список; он содержит только один элемент. Чтобы получить доступ к элементу, вам нужно перебрать результат:

Web modular API

import { getDatabase, ref, onValue } from "firebase/database";

const db = getDatabase();
const dbRef = ref(db, '/a/b/c');

onValue(dbRef, (snapshot) => {
  snapshot.forEach((childSnapshot) => {
    const childKey = childSnapshot.key;
    const childData = childSnapshot.val();
    // ...
  });
}, {
  onlyOnce: true
});

Web namespaced API

ref.once('value', (snapshot) => {
  snapshot.forEach((childSnapshot) => {
    var childKey = childSnapshot.key;
    var childData = childSnapshot.val();
    // ...
  });
});

Этот шаблон может быть полезен, если вы хотите получить все дочерние элементы списка за одну операцию, а не прослушивать дополнительные события, добавленные дочерними элементами.

Сортировка и фильтрация данных

Вы можете использовать класс Query к базе данных в реальном времени для получения данных, отсортированных по ключу, по значению или по значению дочернего элемента. Вы также можете отфильтровать отсортированный результат по определенному количеству результатов или диапазону ключей или значений.

Сортировка данных

Чтобы получить отсортированные данные, начните с указания одного из методов упорядочивания, чтобы определить, как упорядочиваются результаты:

Метод Применение
orderByChild() Упорядочите результаты по значению указанного дочернего ключа или вложенного дочернего пути.
orderByKey() Упорядочить результаты по дочерним ключам.
orderByValue() Упорядочить результаты по дочерним значениям.

Одновременно можно использовать только один метод order-by. Вызов метода упорядочивания несколько раз в одном и том же запросе приводит к ошибке.

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

Web modular API

import { getDatabase, ref, query, orderByChild } from "firebase/database";
import { getAuth } from "firebase/auth";

const db = getDatabase();
const auth = getAuth();

const myUserId = auth.currentUser.uid;
const topUserPostsRef = query(ref(db, 'user-posts/' + myUserId), orderByChild('starCount'));

Web namespaced API

var myUserId = firebase.auth().currentUser.uid;
var topUserPostsRef = firebase.database().ref('user-posts/' + myUserId).orderByChild('starCount');

Это определяет запрос, который в сочетании с дочерним прослушивателем синхронизирует клиента с сообщениями пользователя из пути в базе данных на основе его идентификатора пользователя, упорядоченного по количеству звезд, полученных каждым сообщением. Этот метод использования идентификаторов в качестве ключей индекса называется разветвлением данных. Подробнее о нем можно прочитать в разделе «Структурирование базы данных» .

Вызов метода orderByChild() указывает дочерний ключ, по которому упорядочиваются результаты. В этом случае сообщения сортируются по значению соответствующего дочернего элемента "starCount" . Запросы также можно упорядочить по вложенным дочерним элементам, если у вас есть данные, которые выглядят следующим образом:

"posts": {
  "ts-functions": {
    "metrics": {
      "views" : 1200000,
      "likes" : 251000,
      "shares": 1200,
    },
    "title" : "Why you should use TypeScript for writing Cloud Functions",
    "author": "Doug",
  },
  "android-arch-3": {
    "metrics": {
      "views" : 900000,
      "likes" : 117000,
      "shares": 144,
    },
    "title" : "Using Android Architecture Components with Firebase Realtime Database (Part 3)",
    "author": "Doug",
  }
},

В этом случае мы можем упорядочить элементы нашего списка по значениям, вложенным в ключ metrics , указав относительный путь к вложенному дочернему элементу в нашем вызове orderByChild() .

Web modular API

import { getDatabase, ref, query, orderByChild } from "firebase/database";

const db = getDatabase();
const mostViewedPosts = query(ref(db, 'posts'), orderByChild('metrics/views'));

Web namespaced API

var mostViewedPosts = firebase.database().ref('posts').orderByChild('metrics/views');

Дополнительные сведения о том, как упорядочиваются другие типы данных, см. в разделе Как упорядочиваются данные запроса .

Фильтрация данных

Чтобы фильтровать данные, вы можете комбинировать любой метод ограничения или диапазона с методом упорядочивания при построении запроса.

Метод Применение
limitToFirst() Устанавливает максимальное количество элементов, возвращаемых с начала упорядоченного списка результатов.
limitToLast() Устанавливает максимальное количество элементов, возвращаемых с конца упорядоченного списка результатов.
startAt() Возвращает элементы, которые больше или равны указанному ключу или значению, в зависимости от выбранного метода упорядочения.
startAfter() Возвращает элементы, превышающие указанный ключ или значение, в зависимости от выбранного метода упорядочения.
endAt() Возвращает элементы, меньшие или равные указанному ключу или значению, в зависимости от выбранного метода упорядочения.
endBefore() Возвращает элементы, меньшие указанного ключа или значения, в зависимости от выбранного метода упорядочения.
equalTo() Возвращает элементы, равные указанному ключу или значению, в зависимости от выбранного метода упорядочения.

В отличие от методов упорядочивания, вы можете комбинировать несколько функций ограничения или диапазона. Например, вы можете объединить методы startAt() и endAt() , чтобы ограничить результаты указанным диапазоном значений.

Ограничить количество результатов

Вы можете использовать методы limitToFirst() и limitToLast() , чтобы установить максимальное количество дочерних элементов, которые будут синхронизироваться для данного события. Например, если вы используете limitToFirst() для установки ограничения в 100, изначально вы получаете только до 100 событий child_added . Если в вашей базе данных Firebase хранится менее 100 элементов, для каждого элемента срабатывает событие child_added .

По мере изменения элементов вы получаете события child_added для элементов, которые входят в запрос, и события child_removed для элементов, которые исключаются из него, так что общее число остается равным 100.

В следующем примере показано, как примерное приложение для ведения блога определяет запрос для получения списка из 100 последних сообщений всех пользователей:

Web modular API

import { getDatabase, ref, query, limitToLast } from "firebase/database";

const db = getDatabase();
const recentPostsRef = query(ref(db, 'posts'), limitToLast(100));

Web namespaced API

var recentPostsRef = firebase.database().ref('posts').limitToLast(100);

В этом примере определяется только запрос. Для фактической синхронизации данных необходимо иметь подключенный прослушиватель .

Фильтровать по ключу или значению

Вы можете использовать startAt() , startAfter() , endAt() , endBefore() и equalTo() для выбора произвольных начальных, конечных и эквивалентных точек для запросов. Это может быть полезно для разбивки данных на страницы или поиска элементов с дочерними элементами, имеющими определенное значение.

Как упорядочиваются данные запроса

В этом разделе объясняется, как данные сортируются каждым из методов упорядочивания в классе Query .

orderByChild

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

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

orderByKey

При использовании orderByKey() для сортировки данных данные возвращаются в порядке возрастания ключа.

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

orderByValue

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

Отключить прослушиватели

Обратные вызовы удаляются путем вызова метода off() в ссылке на базу данных Firebase.

Вы можете удалить один прослушиватель, передав его в качестве параметра функции off() . Вызов off() для местоположения без аргументов удаляет всех прослушивателей в этом месте.

Вызов off() родительского прослушивателя не удаляет автоматически прослушиватели, зарегистрированные на его дочерних узлах; off() также должен быть вызван для всех дочерних прослушивателей, чтобы удалить обратный вызов.

Следующие шаги