Android объединяет два вызова Firestore в один обратный вызов

В настоящее время я запрашиваю коллекцию по ссылке Firestore:

val missionsCollection = FirebaseFirestore.getInstance()
    .collection("/customers").document(user.customerId).collection("missions")
    .orderBy("deadline")
    .whereGreaterThanOrEqualTo("deadline", startDate)
    .whereLessThanOrEqualTo("deadline", endDate)
    .whereArrayContains("staffs", user.id)

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

крайний срок: метка времени
сотрудники: массив строк с идентификаторами сотрудников

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

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

Поскольку у меня нет метода с именем .whereArrayIsEmpty("staffs"), я создал в своем документе поле с именем "visibleToAllStaffs", которое является логическим полем, но у меня есть более глубокая проблема. Firebase не разрешает оператор ИЛИ.

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

Итак, мои вопросы:

1) Есть ли собственный способ (используя только firebase) для группировки двух запросов, которые извлекают данные из одной и той же коллекции, имея только один обратный вызов для ошибки и один для успеха (где, в этом случае, успех принесет обе данные)?

2) Если нет, могу ли я решить это с помощью RxJava или Coroutines? Как?

РЕДАКТИРОВАТЬ

Просто дам еще несколько объяснений, мой второй звонок будет:

val missionsCollection = FirebaseFirestore.getInstance()
    .collection("/customers").document(user.customerId).collection("missions")
    .orderBy("deadline")
    .whereGreaterThanOrEqualTo("deadline", startDate)
    .whereLessThanOrEqualTo("deadline", endDate)
    .whereEqualTo("visibleToAllStaffs", true)

Переменные missionsCollection – это объекты типа Query. Для этих объектов я могу вызвать метод get(), который возвращает Задача объекта QuerySnapshot (Task<QuerySnapshot>)

С помощью этого объекта Task<QuerySnapshot> я могу добавить некоторые обратные вызовы (например, успех и ошибка), например:


missionsCollection.get().addOnSuccessListener { documentSnapshot ->
    val missions = documentSnapshot.map { it.toObject(Mission::class.java) }
    // do something with the list
}.addOnFailureListener {
    // Do something
}

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

.merge(getMissionsCollection(), getSecondMissionsCollection())
.addOnSuccessListener { documentSnapshot ->
    val missionsFromBothCalls = documentSnapshot.map { it.toObject(Mission::class.java) }
    // do something with the full list
}.addOnFailureListener {
    // Do something
}

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


person LeonardoSibela    schedule 16.05.2019    source источник


Ответы (2)


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

suspend fun getMissionsCollection() = FirebaseFirestore.getInstance()
     .collection("/customers").document(user.customerId).collection("missions")
     .orderBy("deadline")
     .whereGreaterThanOrEqualTo("deadline", startDate)
     .whereLessThanOrEqualTo("deadline", endDate)
     .whereArrayContains("staffs", user.id)
     .get().await().result

suspend fun getSecondCollection() = FirebaseFirestore.getInstance()
     .
     . 
     .
     .get().await().result

а затем с помощью

launch {
   val missionCollection = getMissionsCollection()
   val secondCollection= getSecondCollection()

   // merge collections or whatever
}
person P.Juni    schedule 16.05.2019
comment
Большое спасибо, но моя самая большая проблема с сопрограммами заключалась в попытке объединить оба обратных вызова, потому что метод get() дает мне Task‹QuerySnapshot›, а не List‹Mission›. Если я отредактирую свой вопрос, объясняя, каким должен быть мой второй запрос, а также немного подробнее о возврате метода get, не могли бы вы помочь мне и с этим? - person LeonardoSibela; 16.05.2019
comment
Не обещаю, давай попробуем :D - person P.Juni; 16.05.2019
comment
Я отредактировал вопрос, насколько мог. Я действительно добавляю вам то, что вы уже ответили, но свежая информация о сопрограммах затрудняет понимание некоторых концепций, таких как слияние двух функций приостановки, которые возвращают наблюдаемое - person LeonardoSibela; 16.05.2019
comment
что вы подразумеваете под наблюдаемым? Вы хотите увидеть, изменились ли данные в базе данных? Видели ли вы обновления в реальном времени в firestore -› firebase.google.com/docs/firestore /запрос-данные/прослушивание - person P.Juni; 16.05.2019
comment
На самом деле я использовал эту реализацию: firebase.google.com/docs/firestore /запрос-данные/получить-данные - person LeonardoSibela; 16.05.2019

Привет, брат, ты можешь увидеть мой пример.

    private void firebaseTasks() { 

    String audioLink = ""
    String videoLink = ""

    Task taskA = firebase.collection("collectionA").document("docPath")
            .get().addOnSuccessListener(documentSnapshot -> {
                arrayA.addAll((ArrayList<String>) Objects.requireNonNull(documentSnapshot.get("your Field Name")));
                recyclerViewAdapter.notifyDataSetChanged();
            });
    Task taskB = firebase.collection("collectionB").document("docPath1")
            .get().addOnSuccessListener(documentSnapshot -> arrayB.addAll((ArrayList<Boolean>) documentSnapshot.get("your Field Name")));

    Task taskC = firebase.collection("collectionC").document("docPath2")
            .get().addOnSuccessListener(documentSnapshot -> arrayC.addAll((ArrayList<String>) documentSnapshot.get("your Field Name")));

    Task taskD = firebase.collection("collectionD").document("docPath3")
            .get().addOnSuccessListener(documentSnapshot -> arrayD.addAll((ArrayList<String>) documentSnapshot.get("your Field Name")));

    Task taskE = firebase.collection("collectionE").document("docPath4")
            .get().addOnSuccessListener(documentSnapshot -> videoLink = documentSnapshot.getString("your Field Name"));

    Task taskF = firebase.collection("collectionF").document("docPath5")
            .get().addOnSuccessListener(documentSnapshot -> audioLink = documentSnapshot.getString("your Field Name"));

    Tasks.whenAllComplete(taskA, taskB, taskC, taskD, taskE,taskF)
            .addOnCompleteListener(task -> {
                // The task is completed 
            });
}

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

    public class FirebaseTaskListExample {

FirebaseFirestore firestore = FirebaseFirestore.getInstance();

    // only these import carefully
    //    import com.google.android.gms.tasks.Task;
    //import com.google.android.gms.tasks.Tasks;

    public void generateTasks(){
    ArrayList<Model> collection = new ArrayList<>();
    List<Task<?>> taskArray = new ArrayList<>();

    collection.add(new Model("aCol","aDoc","aChild"));
    collection.add(new Model("bCol","bDoc","bChild"));

    for(int i = 0; i < collection.size(); i++){
        Task<DocumentSnapshot> task =                 getTask(collection.get(i).collectionName,collection.get(i).docName);
        int finalI = i;
        task.addOnSuccessListener(new OnSuccessListener<DocumentSnapshot>() {
            @Override
            public void onSuccess(DocumentSnapshot documentSnapshot) {
                Log.e("Firebase Task 
    Info",documentSnapshot.getString(collection.get(finalI).childDoc));
            }
        });
        taskArray.add(task);
    }

    // after add all tasks set callback

    Tasks.whenAllComplete(taskArray).addOnSuccessListener(new 
     OnSuccessListener<List<Task<?>>>() {
        @Override
        public void onSuccess(List<Task<?>> tasks) {
         // all task is completed
            Log.d("Firebase Task Info","Task Success");
        }
    }).addOnFailureListener(new OnFailureListener() {
        @Override
        public void onFailure(@NonNull Exception e) {

        }
    });

}

    public Task getTask(String col, String doc){
    return firestore.collection(col).document(doc).get();
}

    class Model{
    String collectionName;
    String docName;
    String childDoc;
    Model(String collectionName, String docName,String childDoc){
        this.collectionName = collectionName;
        this.childDoc = childDoc;
        this.docName = docName;
    }
}

}

person Vishal    schedule 12.02.2020
comment
Прочтите это. - person Mridul; 12.02.2020
comment
Могу ли я поместить этот код в цикл for? для того, чтобы генерировать количество задач программно. - person Parth Patel; 14.02.2020
comment
Да, вы можете сгенерировать количество задач программно. Я также обновил код, вы можете увидеть пример. Ты - person Vishal; 14.02.2020