Поскольку в Firestore нет логического оператора OR
, я пытаюсь объединить 2 отдельных запроса локально.
Теперь мне интересно, как я могу поддерживать правильный порядок результатов. Когда я выполняю 2 запроса независимо, я не могу указать результаты конкретно (по крайней мере, не в том порядке, в котором я получаю результаты из Firestore с помощью метода orderBy
).
Моя идея заключалась в том, чтобы поместить второй запрос в onSuccessListener
первого запроса. Это плохая идея с точки зрения производительности?
public void loadNotes(View v) {
collectionRef.whereLessThan("priority", 2)
.orderBy("priority")
.get()
.addOnSuccessListener(new OnSuccessListener<QuerySnapshot>() {
@Override
public void onSuccess(QuerySnapshot queryDocumentSnapshots) {
for (QueryDocumentSnapshot documentSnapshot : queryDocumentSnapshots) {
Note note = documentSnapshot.toObject(Note.class);
//adding the results to a List
}
collectionRef.whereGreaterThan("priority", 2)
.orderBy("priority")
.get()
.addOnSuccessListener(new OnSuccessListener<QuerySnapshot>() {
@Override
public void onSuccess(QuerySnapshot queryDocumentSnapshots) {
for (QueryDocumentSnapshot documentSnapshot : queryDocumentSnapshots) {
Note note = documentSnapshot.toObject(Note.class);
//adding the results to a List
}
}
});
}
});
}
Чтобы объединить 2 отдельных запроса локально, я рекомендую вам использовать метод Tasks.whenAllSuccess()
. Вы можете добиться этого, используя следующие строки кода:
FirebaseFirestore rootRef = FirebaseFirestore.getInstance();
Query firstQuery = rootRef...
Query secondQuery = rootRef...
Task firstTask = firstQuery.get();
Task secondTask = secondQuery.get();
Task combinedTask = Tasks.whenAllSuccess(firstTask, secondTask).addOnSuccessListener(new OnSuccessListener<List<Object>>() {
@Override
public void onSuccess(List<Object> list) {
//Do what you need to do with your list
}
});
Как видите, при переопределении метода onSuccess()
результатом является list
объектов, которые имеют точный порядок задач, которые были переданы в качестве аргументов методу whenAllSuccess()
.
Есть и другой подход - использовать метод Tasks.continueWith()
. Но в зависимости от варианта использования вашего приложения вы можете использовать метод eiter whenAllSuccess()
или метод continueWith()
. Пожалуйста, посмотрите здесь официальная документация.
Класс QuerySnapshot, как и любой другой класс в Java, расширяет класс Object
. Объект класса - это корень иерархии классов. Каждый класс имеет объект в качестве суперкласса. Итак, QuerySnapshot
- это объект. В этом случае QuerySnapshot
является родственным типом, поэтому не стесняйтесь использовать List<QuerySnapshot>
.
Хорошо, но это лучше, чем заливка objects
в onSuccess
, или это не имеет значения?
Нет никакой разницы. Пожалуйста, посмотрите это Почта.
Я просто пытаюсь понять, почему он вообще возвращает это как список объектов. Это потому, что я могу передавать различные задачи в whenAllSuccess?
Он возвращает список объектов, потому что вы вообще не указываете правильный класс. Таким образом, общий класс для списка установлен как корневой класс всех объектов. Каждый класс в Java "является" объектом. Когда вы используете универсальные шаблоны, вы указываете точный тип содержащихся объектов, поэтому список будет содержать объекты типа QuerySnapshot
, а не Object. Верно?
Верно, но метод get
на CollectionReference
возвращает Task<QuerySnapshot
, и я ничего не определяю. Вот почему мне интересно, почему в методе whenAllSucess
больше свободы. Как новичок, я просто задаюсь вопросом, разумна ли причина «Я не хочу приводить объекты» для сохранения ее в переменной с универсальным типом.
Да, ты прав. Это происходит потому, что onComplete () определен как onComplete(Task<T> task)
, и когда вызывается на CollectionReference
, он возвращает только Task<QuerySnapshot>
, но при вызове на Tasks.whenAllSuccess(firstTask, secondTask)
может возвращать список различных типов объектов. Android Studio не знает, что это за объекты, и поэтому возвращает общий список объектов, верно?
Хорошо, я понимаю, почему Android Studio устанавливает такой тип. Новичку иногда просто сложно понять лучшие практики, и мне интересно, разумно ли определение универсального типа только ради читабельности кода (как я делаю в своем подходе). Но из того, что я читал об общих типах, можно сказать, что одним из преимуществ является отказ от приведения типов, поэтому это кажется разумным.
Да, это. Вы можете пойти по этому пути. Как вы думаете, мой ответ вам помог?
Да, это решило вопрос и было очень полезно! Спасибо!
@AlexMamo Привет, я могу сделать нумерацию страниц здесь? ограничение 10 на оба запроса? и получить 20 результатов в комбинированном задании?
Как в таких случаях рассчитывается плата за чтение? Это для обоих результатов или для комбинированного? Как и в первом фильтре, мы получаем 20 результатов, а во втором, после дополнительной фильтрации, мы получаем 10, поэтому с нас будет взиматься плата за 10, 20 или 30?
@SurabhiChoudhary Вы будете платить за общее количество элементов, возвращаемых запросами.
Спасибо @AlexMamo за такой быстрый ответ. Действительно обязан. Последний вопрос, можно ли это использовать со слушателями моментальных снимков?
@SurabhiChoudhary Нет, только когда вы используете вызов get()
, поскольку get()
возвращает объект Task
.
@SurabhiChoudhary, как вы передали его в QuerySnapshot, не могли бы вы опубликовать пример, пожалуйста.
@SabeehUlHaq Я не уверен, что понимаю вопрос. Если вам сложно это реализовать, задайте новый вопрос и покажите нам, что вы пробовали. В этом вам помогут я или другие разработчики.
@AlexMamo, значит, 27 февраля 2020 года ответ будет 10 или 30?
@fkvestak 10 или 30 что?
@AlexMamo кто-то спросил, будем ли мы платить за 10, 20 или 30 результатов в его примере? Скажем, мой первый запрос возвращает 200 результатов, второй - 50, третий - 10. Я визуализирую эти 10 элементов, потому что они удовлетворяют всем 3 запросам. Но сколько я буду платить? 10 прочитанных документов или 260?
@fkvestak Вам всегда будет поручено количество операций чтения, равное количеству возвращаемых элементов. Если у вас, например, три запроса, и каждый из них возвращает три документа, вам будет начислено 9 чтений документов.
@AlexMamo, вот чего я боялся. Это действительно плохо, что делает Firestore практически бесполезным даже для базового приложения TODO.
@fkvestak Я думаю, что этот статья предоставит еще больше информации. На самом деле это не то, что вы говорите.
@AlexMamo, ваша статья выглядит очень красиво, я займусь этим сегодня вечером;)
@fkvestak Приятно это слышать;)
@AlexMamo У вас есть информация / опыт выполнения 2 запросов? Все всегда говорят, что лучше всего запускать 1 запрос и дублировать данные. Я, например, создаю приложение для знакомств и хочу показывать рекомендации профиля клиентам. Мне нужно убедиться, что они еще не видели профиль или он им понравился. Я сохраняю лайки пользователя в собственной коллекции. Поэтому я бы получил 5 профилей, взял идентификаторы и проверил, есть ли они в коллекции пользователей (10 считываний). Для меня это имеет больше смысла, чем повторное сохранение всех данных где-то еще на пользователе, я все равно не смогу получить свои результаты. что ты думаешь
@MarcelDz Может сработать в вашем сценарии, но если у вас есть что-то особенное, кроме выполнения нескольких запросов (что вполне нормально), разместите новый вопрос здесь, в Stackoverflow, используя его собственный MCVE, чтобы я и другие разработчики Firebase могли вам помочь.
@AlexMamo, хорошо, я так и сделал, stackoverflow.com/questions/66884058/… с нетерпением ждет вашего вклада. Привет Марсель
Большое спасибо! На самом деле я нашел те же решения в Интернете, но я был очень не уверен, верны ли они, потому что нашел так мало примеров. Теперь, когда вы называете те же самые методы, у меня есть страховка. Одно: я новичок, поэтому у меня проблемы с дженериками, но я заметил, что, когда я пишу
Task<List<QuerySnapshot>> combinedTask
вместо объявления типа какTask
, я получаюList<QuerySnapshot>
в OnSucess вместоList<Object
, что делает его более удобным. Это правильный способ объявления типа?