Пагинация Firestore: обновленные данные в реальном времени неожиданно перемещаются между страницами

Я использую Firestore и запрашиваю данные с orderBy, установленным на updatedAt в descending порядке, и ограничиваю результаты до 2 элементов на странице (в целях тестирования). Однако я сталкиваюсь с неожиданным поведением при обновлении данных.

Когда я обновляю самый старый элемент на второй странице, чтобы он имел последнюю метку времени updateAt, он перемещается на первую позицию на первой странице, фактически удаляя его со второй страницы. Кроме того, второй элемент на первой странице также удаляется.

Вот расшифровка ситуации:

Начальное состояние:

Page 1: ['item1', 'item2'] (no `startAfter` specified)
Page 2: ['item3', 'item4'] (starting after 'item2')

После обновления элемента 4 (updatedAt изменен на последний):

Page 1: ['item4', 'item1']
Page 2: ['item3']

Однако мой ожидаемый результат:

Page 1: ['item4', 'item1']
Page 2: ['item2', 'item3']
// Query example
const query = firestore.collection('myCollection')
                     .where('tests', 'array-contains', `test`)
                     .orderBy('updatedAt', 'desc')
                     .limit(2)
                     .startAfter(startAfterVar)
                     .onSnapshot(snapshot => { 
                       // Do something to store
                     });

Мы очень ценим вашу помощь в решении этой проблемы. Спасибо!

Обновлено 1:

Вопрос: То есть вы фактически утверждаете, что элемент2 удален из базы данных? Или просто не отображается?

О: По сути, item2 не отображается и не удаляется из БД.

Вопрос: Можете ли вы отредактировать свой вопрос и добавить структуру базы данных в виде снимка экрана?

А: Структура данных:

myCollection (collection)
  |
  --- item1 (document)
       |
       --- tests: [array string]
       --- updatedAt: [timestamp]
       --- otherFields: ...

Обновлено 2:

Я реализую бесконечную нумерацию страниц в своем запросе

Обновление 3:

Обновлен скриншот коллекции тем.

Мой фактический запрос

// Query example
const query = firestore.collection('topics')
                     .where('members', 'array-contains', userID)
                     .orderBy('lastLoggedAt', 'desc')
                     .limit(2)
                     .startAfter(startAfterVar)
                     .onSnapshot(snapshot => { 
                       // Do something to store
                     });

То есть вы по сути утверждаете, что item2 удален из базы данных? Или просто не отображается? Можете ли вы отредактировать свой вопрос и добавить структуру базы данных в виде снимка экрана?

Alex Mamo 12.04.2024 09:23

Привет @Alex Mamo, я только что обновил свой вопрос в Обновлении 1, этого достаточно? Спасибо :bow:

Long Nguyen 12.04.2024 09:53

@Алекс Мамо Извините, но, похоже, произошло недоразумение. В настоящее время я использую Firestore, а не базу данных Realtime. В Firestore нет экспорта JSON как базы данных реального времени.

Long Nguyen 12.04.2024 11:31

Извини за это. Вместо описания того, как выглядит ваша база данных, отредактируйте свой вопрос и добавьте его снимок экрана.

Alex Mamo 12.04.2024 11:37

@AlexMamo Спасибо за ваш ответ. Я обновил скриншот документа в обновлении 3.

Long Nguyen 12.04.2024 12:12

Привет @BrianBurton, я ценю твой ответ. Вы предполагаете, что достаточно указать номер страницы для параметра startAt, а не передавать последний снимок из предыдущего запроса? У меня сложилось впечатление, что startAt также будет частью результирующего набора. Не могли бы вы подробно описать свое решение в ответе? Я пробую ваше предложение.

Long Nguyen 13.04.2024 08:07

Нет, извини, я ошибся. Я опубликовал ответ, который должен решить вашу проблему. Решение состоит в том, чтобы использовать startAt(), но я забыл, что вы не можете передавать индекс.

Brian Burton 13.04.2024 10:32
Интеграция Angular - Firebase Analytics
Интеграция Angular - Firebase Analytics
Узнайте, как настроить Firebase Analytics и отслеживать поведение пользователей в вашем приложении Angular.
0
7
69
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Отказ от ответственности: нумерация страниц с прослушивателями в реальном времени сложна.

То, что вы описываете, является ожидаемым поведением. Начиная с этого набора данных:

[item1, item2, item3, item4]

Когда вы настраиваете первый прослушиватель, вы читаете два документа для item1 и item2. Затем вы создаете второй прослушиватель, который начинается после item2, и затем он читает item3 и item4.

Теперь измените item4, чтобы стать первым в порядке сортировки. Итак, получается:

[item4, item1, item2, item3]

Итак, первый слушатель теперь видит item4 и item1. Но второй прослушиватель по-прежнему запускается после item2, поэтому он видит только item3, а вы эффективно скрываете item2.


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

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

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

Long Nguyen 13.04.2024 08:17

Если я правильно понимаю ваш вопрос, проблема в использовании startAfter(), которое исключает указанный документ из результатов.

В документации говорится:

Используйте методы startAt() или startAfter(), чтобы определить начальную точку запроса. Метод startAt() включает начальную точку, а метод startAfter() исключает ее. Например, если вы используете startAt(A) в запросе, он возвращает весь алфавит. Если вместо этого вы используете startAfter(A), он возвращает B-Z.

Если вы измените startAfter() на startAt(), он должен вернуть элемент2.

// Query example
const query = firestore.collection('topics')
    .where('members', 'array-contains', userID)
    .orderBy('lastLoggedAt', 'desc')
    .limit(2)
    .startAt(startAfterVar)
    .onSnapshot(snapshot => { 
  // Do something to store
  });

Привет @ Брайан, спасибо за ответ. Извините, но кажется странным, что при использовании startAt вместо startAfter страница 2 включает [item2, item3] после того, как страница 1 содержала [item1, item2]

Long Nguyen 14.04.2024 12:14

Если вы используете startAt, вы должны использовать startAt(item3) для страницы 2.

Brian Burton 14.04.2024 17:24

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

Long Nguyen 15.04.2024 02:54

Другие вопросы по теме