2

I've seen this question before for an earlier version of Firebase that is no longer applicable for Firebase Cloud Firestore.

I would like to make a query that checks a document's field's value against a list of potential values, something like whereEqualTo(someField, a OR b OR c OR ... OR n). Specifically, I have a list of potential values for a single field of type Reference and I want my query to return all documents in the specified collection whose value of the field is equal to one of the potential values in the list. I am also using .limit(10) because I am paginating data.

So for example, if my list contained the References [ref1, ref2, ref3] then the query would be similar to joining these 3 queries:

Query query1 = mDatabase.collection("myCollection").whereEqualTo("refField", ref1);
Query query2 = mDatabase.collection("myCollection").whereEqualTo("refField", ref2);
Query query3 = mDatabase.collection("myCollection").whereEqualTo("refField", ref3);

On the documentation, under the "Limitations" section, it mentions:

Logical OR queries. In this case, you should create a separate query for each OR condition and merge the query results in your app.

So maybe the best way would be to merge query results, but how could this be done, and with limiting to 10 results?

Note that I am working with Java for Android Development.

Alex Mamo
  • 130,605
  • 17
  • 163
  • 193
Paradox
  • 4,602
  • 12
  • 44
  • 88

1 Answers1

2

I would like to make a query that checks a document's field's value against a list of potential values.

Unfortunately, there is no query in Firestore that can help you achieve this. What can you do instead is to get all the documents beneath your myCollection collection and check the value of document field you need against a list of potential values like this:

FirebaseFirestore rootRef = FirebaseFirestore.getInstance();
CollectionReference myCollectionRef = rootRef.collection("myCollection");
myCollectionRef.get().addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
    @Override
    public void onComplete(@NonNull Task<QuerySnapshot> task) {
        if (task.isSuccessful()) {
            List<String> yourList = new ArrayList<>();
            for (QueryDocumentSnapshot document : task.getResult()) {
                DocumentReference reference = document.getDocumentReference("refField");
                String stringReference = reference.getPath();
                if (yourList.contains(stringReference)) {
                    Log.d(TAG, "Reference found!");
                }
            }
        }
    }
});

I assumed your list contains document references of type String. As you can see, we need to serialize a DocumentReference objects that we get from the database by using getPath() method to get a String that describes the document's location in the database. To deserialize that path String back into a DocumentReference object, use FirebaseFirestore.getInstance().document(path) method.

PS. If you are interested about pagination, this is how you can paginate queries by combining query cursors with the limit() method. I also recommend you take a look at this video for a better understanding.

Alex Mamo
  • 130,605
  • 17
  • 163
  • 193
  • The issue I have is that this fetches and loops over every document in the collection. So if there were a large amount of documents in the collection that do not contain one of the references, before the first document containing one of the references, then it could take a long time before it finds a reference. It's basically O(n) and if n being the amount of documents is large, this would be slow, defeating the purpose of using a query. Add in pagination and this is multiplied for every time more documents are fetched. – Paradox Jul 17 '18 at 00:47
  • That's not correct. The above code is just an example. If you are using pagination, as you said you intend to, you do not query the entire database, you'll use the limit() method that will help you prevent that. So that's why you are using pagination to load data in smaller chunks. Even if you'll like to query the entire database, remember that Firestore scales automatically, right? See [here](https://stackoverflow.com/questions/47319139/should-i-use-redundancy-or-a-simple-query-on-a-large-dataset-with-firebase-cloud). – Alex Mamo Jul 17 '18 at 08:22
  • One more thing to note, one of the Firebase engineers said and I quote, "It is impossible to build a slow query in Firestore". So, the performance comes from the new indexing capabilities. So don't worry about scalability. – Alex Mamo Jul 17 '18 at 08:29
  • Even if I use the limit(10) method, if there were 1000 documents and only the last one had the reference, I'd need to make the query 100 times, looping through all 10 documents each time before I reached the document with the reference. Firestore's indexing capabilities won't help because it doesn't know that I'm looking for a specific reference on the client, right? Hence although it isn't a "slow" query to get each 10 documents, it's still slow because it has to do it 100 times and loop through each 10 on the client. So the worst-case runtime of this is the same as if I fetched every doc. – Paradox Jul 18 '18 at 03:01
  • The reason it's so slow is because it's not limiting the documents fetched during the query (which I am trying to do) through a whereEqualTo or something to that effect. – Paradox Jul 18 '18 at 03:03
  • What you are trying to say is correct. If the reference that you are looking for is in the last document, then 100 queries are needed. It's the normal behaviour because you cannot know where the reference is. In this case, I recommend you another approach, which would be to use a reverse look-up. Create a new collection named `references` and add as documents, each reference separately. This is a common practice in NoSql databases. So in this case, you'll only need to get the data in very simple way, `db.collection("references").document("desiredReference ");`, right? – Alex Mamo Jul 18 '18 at 08:37