Firestore - Merging Two Queries Locally

Firestore - Merging two queries locally

To merge 2 separate queries locally, I recommend you to use Tasks.whenAllSuccess() method. You can achieve this, using the following lines of code:

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
}
});

As you can see, when overriding the onSuccess() method the result is a list of objects which has the exact order of the tasks that were passed as arguments into the whenAllSuccess() method.

There is also another approach and that would be to use Tasks.continueWith() method. But according to the use-case of your app, you can use eiter whenAllSuccess() method or continueWith() method. Please see here the official documentation.

Combining multiple Firestore queries to get specific results (with pagination)

Say you want to show 10 results on a page. You will need to get 10 results for each of the subqueries, and then merge the results client-side. You will be overreading quite a bit of data, but that's unfortunately unavoidable in such an implementation.

The (preferred) alternative is usually to find a data model that allows you to implement the use-case with a single query. It is impossible to say generically how to do that, but it typically involves adding a field for the OR condition.

Say you want to get all results where either "fieldA" is "Red" or "fieldB" is "Blue". By adding a field "fieldA_is_Red_or_fieldB_is_Blue", you could then perform a single query on that field. This may seem horribly contrived in this example, but in many use-cases it is more reasonable and may be a good way to implement your OR use-case with a single query.

How to merge two queries using Firestore - Swift

There are many ways to accomplish this; here's one option.

To provide an answer, we have to make a couple of additions; first, we need somewhere to store the data retrieved from firebase so here's a class to contains some chat information

class ChatClass {
var from = ""
var to = ""
var msg = ""
var timestamp = 0

convenience init(withDoc: DocumentSnapshot) {
self.init()
self.from = withDoc.get("from") as! String
self.to = withDoc.get("to") as! String
self.msg = withDoc.get("msg") as! String
self.timestamp = withDoc.get("timestamp") as! Int
}
}

then we need a class level array to store it so we can use it later - perhaps as a tableView dataSource

class ViewController: NSViewController {
var sortedChatArray = [ChatClass]()

The setup is we have two users, Jay and Cindy and we want to retrieve all of the chats between them and sort by timestamp (just an Int in this case).

Here's the code that reads in all of the chats from one user to another creates ChatClass objects and adds them to an array. When complete that array is passed back to the calling completion handler for further processing.

func chatQuery(from: String, to: String, completion: @escaping( [ChatClass] ) -> Void) {
let chatsColl = self.db.collection("chats") //self.db points to my Firestore
chatsColl.whereField("from", isEqualTo: from).whereField("to", isEqualTo: to).getDocuments(completion: { snapshot, error in
if let err = error {
print(err.localizedDescription)
return
}

guard let docs = snapshot?.documents else { return }

var chatArray = [ChatClass]()

for doc in docs {
let chat = ChatClass(withDoc: doc)
chatArray.append(chat)
}

completion(chatArray)
})
}

Then the tricky bit. The code calls the above code which returns an array The above code is called again, returning another array. The arrays are combined, sorted and printed to console.

func buildChatArray() {
self.chatQuery(from: "Jay", to: "Cindy", completion: { jayCindyArray in
self.chatQuery(from: "Cindy", to: "Jay", completion: { cindyJayArray in
let unsortedArray = jayCindyArray + cindyJayArray
self.sortedChatArray = unsortedArray.sorted(by: { $0.timestamp < $1.timestamp })

for chat in self.sortedChatArray {
print(chat.timestamp, chat.from, chat.to, chat.msg)
}
})
})
}

and the output

ts: 2  from: Cindy  to: Jay  msg: Hey Jay, Sup.
ts: 3 from: Jay to: Cindy msg: Hi Cindy. Not much
ts: 9 from: Jay to: Cindy msg: Talk to you later

Is it possible to merge an unknown number of firestore queries in android?

I've tested this on my current project, it works fine (tested for only two documents with a followingList.size of 2 elements)

public void testMy() {
db = FirebaseFirestore.getInstance();
List<String> followingList = new ArrayList<>();
followingList.add("tag1"); followingList.add("tag2"); followingList.add("tag3"); // ...
List<Task> taskList = new ArrayList<>();

for (String s : followingList) {
Query query = db.collection("invites")
.whereEqualTo("uid", s)
.orderBy("imageUrl_1", Query.Direction.ASCENDING);; //followingList is a list of Strings that I want to query
Task<QuerySnapshot> task = query.get();
taskList.add(task);
}

Tasks.whenAllSuccess(taskList.toArray(new Task[taskList.size()])).addOnSuccessListener(new OnSuccessListener<List<Object>>() {
@Override
public void onSuccess(List<Object> objects) {
@SuppressWarnings("unchecked")
List<QuerySnapshot> querySnapshots = (List<QuerySnapshot>)(List<?>) objects;
for (QuerySnapshot qs : querySnapshots) {
for (QueryDocumentSnapshot qds : qs) {
Log.d(TAG, qds.getData().toString());
}
}
}
});
}

Run multiple Firestore queries and wait for all of them to be completed

You can simply merge all your 4 separate queries locally, using Tasks's whenAllSuccess() method. You can achieve this, using the following lines of code:

Task t1 = Query1.get();
Task t2 = Query2.get();
Task t3 = Query3.get();
Task t4 = Query4.get();

Task combinedTask = Tasks.whenAllSuccess(t1, t2, t3, t4).addOnSuccessListener(new OnSuccessListener<List<Object>>() {
@Override
public void onSuccess(List<Object> list) {
//Do what you need to do with your list
}
});

As you can see, when overriding the onSuccess() method the result is a list of objects that you get from those queries.

The Cloud Firestore client, already runs all network operations in a background thread. This means that all operations take place without blocking your main thread. Putting it in an AsyncTask does not give any additional benefits.



Related Topics



Leave a reply



Submit