Что я пытаюсь добавить sh?
В настоящее время я сталкиваюсь с множеством проблем, связанных с реализацией обновленного бесконечного списка прокрутки в реальном времени с бэкэндом Firestore.
В моем приложении я хочу отображать комментарии (например, на YouTube или других сайтах социальных сетей) для пользователя. Поскольку количество комментариев в коллекции может быть довольно большим, я вижу возможность разбить коллекцию на страницы, получая обновления в режиме реального времени на основе снимков. Поэтому я изначально загружаю x
комментариев с возможностью загружать до x
дополнительных элементов всякий раз, когда пользователь нажимает кнопку. На изображении ниже x = 3
.
Стандартное решение
На основе других вопросов SO I выяснил, что для реализации такого поведения предполагается использовать методы .limit()
и .startAfter()
. Таким образом, первая страница загружается как:
query = this
.collection
.orderBy('date', descending: true)
.limit(pageSize);
query.snapshots().map((QuerySnapshot snap) {
lastVisible = snap.documents.last;
// convert the DocumentSnapshot into model object
});
Все дополнительные страницы загружаются с помощью следующего кода:
query = this.collection
.orderBy('date', descending: true)
.startAfterDocument(lastVisible)
.limit(pageSize);
Кроме того, я хотел бы добавить, что этот код находится в класс репозитория, который используется с шаблоном BLo C, аналогичным коду, показанному в Учебнике Феликса Ангелова по флаттеру Todos . В то время как Феликс использует простой список трепетания, чтобы показать элементы, у меня есть список страниц с комментариями на основе данных, предоставленных их BLoC. Обратите внимание, что каждый BLo C получает доступ к общему хранилищу (части кода хранилища показаны ниже).
Проблема со стандартным решением
С указанным кодом выше я вижу несколько проблем:
- Если комментарий вставлен в середину упорядоченной коллекции (как это не важно), добавленный комментарий отображается из-за потока, предоставленного снимком. Однако другой уже существующий комментарий больше не отображается из-за оператора
.limit()
в запросе. Можно увеличить предел на единицу, но я не уверен, как редактировать запрос снимка. В случае, если редактирование запроса моментального снимка невозможно, можно создать новый (и больший) запрос, но это потребует дополнительных операций чтения. - Аналогично 1. Если комментарий в середине удаляется, снимок вернет список, который больше не содержит удаленный комментарий, однако появляется другой комментарий (который уже покрыт другой страницей). Например, в сценарии, показанном на рисунке выше, загружаются 5 комментариев. Предполагая, что комментарий 3 удален, комментарий 2 будет показан дважды.
Улучшение стандартного решения
Основываясь на этих двух проблемах, рассмотренных выше, я решил, что этого решения недостаточно, и реализовал решение, которое сначала загружается x
штук при получении двух «интервальных» документов. Затем создается запрос, который выбирает требуемые элементы в интервале с использованием .startAtDocument()
и .endAtDocument()
, что исключает оператор .limit()
.
DocumentSnapshot pageStartDocument;
DocumentSnapshot pageEndDocument;
Future<Stream<List<Comment>>> comments() async {
// This fetches the first and next Document as initialization
// (maybe should be implemented in constructor)
if (pageStartDocument == null) {
Query query = collection
.orderBy('date', descending: true)
.limit(pageSize);
QuerySnapshot snap = await query.getDocuments();
pageStartDocument = snap.documents.first;
pageEndDocument = snap.documents.last;
} else {
Query query = collection
.orderBy('date', descending: true)
.startAfterDocument(pageEndDocument)
.limit(pageSize);
QuerySnapshot snap = await query.getDocuments();
pageStartDocument = snap.documents.first;
pageEndDocument = snap.documents.last;
}
// This fetches a subcollection of elements from the collection
// with the tradeof of double the reads
Query query = this
.collection
.orderBy('date', descending: true)
.startAtDocument(pageStartDocument)
.endAtDocument(pageEndDocument);
return query.snapshots().asyncMap((QuerySnapshot snap) async {
// convert the QuerySnapshot into model objects
});
Как указано в коде, это решение имеет следующее недостаток:
- Поскольку для получения
pageStartDocument
и pageEndDocument
требуется запрос, число операций чтения удваивается, поскольку при создании второго запроса все данные снова считываются. Влияние на производительность можно пренебречь, потому что я считаю, что данные кэшируются, однако иметь вдвое большую стоимость чтения базы данных.
Вопрос:
Поскольку я не только реализуя нумерацию страниц, но и обновления в реальном времени (с добавлением коллекций), оператор .limit()
в моем случае не работает.
Как реализовать разбиение на страницы с обновлениями в реальном времени (без двойного чтения)?
Примечания:
Я наблюдал, как Тодд Керпельман пожирает огромного липкого медведя, объясняя нумерацию страниц , но в видео это выглядит не так тривиально (и было отмечено, что компромисс может потребоваться).
Если есть код с моей стороны обязательно скажите так в комментариях.
Для сценария комментариев не имеет смысла вставлять элемент в середину (отсортированной) коллекции. Однако я хотел бы понять, как это должно быть реализовано, если сценарий требует такой возможности.