38

How to get data by multiple values of one field? For example, I have database with posts and I want to query for all posts where blogId is 1 or 2, sorting by timestamp.

collection("posts").whereEqualTo("blogId", "1")
.whereEqualTo("blogId", 2).orderBy("timestamp", Query.Direction.DESCENDING).limit(50)

Code above is not working :(

How to achieve this? Regards :)

Skye
  • 1,399
  • 2
  • 12
  • 25

9 Answers9

38

Firestore now supports "IN" queries for this purpose.

The query would look like this:

database.collection("collectionName").where("fieldName", "in", ["fieldValue1", "fieldValue2"]);

You can have up to 10 values (fieldValueX) to check "IN" of.


The code OP desired would be as follows:

database.collection("posts").where("blogId", "in", ["1", "2"]); 
Albert Renshaw
  • 16,406
  • 17
  • 99
  • 182
18

You could combine the Observables and return as one

orQuery(){

    const $one = this.afs.collection("posts", ref => ref.where("blogId","==","1")).valueChanges();
    const $two = this.afs.collection("posts", ref => ref.where("blogId","==","2")).valueChanges();

    return combineLatest($one,$two).pipe(
        map(([one, two]) => [...one, ...two])
    )
}

getOr(){
    this.orQuery().subscribe(data => console.log(data))
}
phicon
  • 3,313
  • 5
  • 29
  • 59
  • 3
    Note that final result might contain duplicates in this approach, depending on the query (in this exact scenario, there won't be duplicates sine same field is used for query with different values). – Lahiru Chandima Oct 24 '20 at 03:15
  • It may be hard if we use multi fields like your answer to paginate data. – Le Khiem Nov 11 '20 at 07:30
  • really good clear answer - many thx - a shame this is the best approach and Google can't provide a better solution – danday74 May 19 '22 at 14:53
7

Firebase has listened to our requests and they have included IN query from 7 Nov, 2019. It's a kind of OR query, where you can have upto 10 OR filters.

For android:

collection("posts").whereIn("blogId", Arrays.asList("1", "2"))
.orderBy("timestamp", Query.Direction.DESCENDING).limit(50);

Firebase documentation

hkchakladar
  • 711
  • 1
  • 7
  • 15
5

OR operator is not accepted in firebase firestore:

Cloud Firestore provides limited support for logical OR queries. The in, and array-contains-any operators support a logical OR of up to 10 equality (==) or array-contains conditions on a single field. For other cases, create a separate query for each OR condition and merge the query results in your app.

Queries in Cloud Firestore, Query limitations

Normally using firebase syntax you can call two collections:

const res1 = async collection("posts", ref => ref.where('blogId', '==', 1).get();
const res2 = async collection("posts", ref => ref.where('blogId', '==', 2).get();

and you can merge the results before serving to the view.

But in this case where you have blogIds you can use this syntax: collection("posts").orderBy('blogId').startAt(1).endAt(2);

e.d

ego
  • 3,765
  • 1
  • 34
  • 36
edyrkaj
  • 69
  • 1
  • 3
4

I couldn't find any documentation for the ability to OR together where conditions. But you can rephrase your requirement on the blogId as follows:

WHERE blogId > 0 AND blogId < 3

Try this code:

collection("posts")
.where("blogId", ">", "0")
.where("blogId", "<", "3")
.orderBy("timestamp", Query.Direction.DESCENDING)
.limit(50)
Tim Biegeleisen
  • 451,927
  • 24
  • 239
  • 318
  • 4
    Yeah, but my case is little more complicated. I have an app, where user can subscribe to some blogs. So I need to retrieve all latest posts for few ids (it can be 2,5 or more ids). The ids are autogenerated (for wordpress for example - 831cacax613das) or are from blogger (for blogspot blogs - 12320183127312307212). I'm currently using firebase database with elasticsearch, but I'm interested in migrating to Firestore. – Skye Oct 13 '17 at 09:42
  • 1
    Check here for how to work with collections: https://cloud.google.com/firestore/docs/solutions/arrays – Tim Biegeleisen Oct 13 '17 at 09:45
  • Hmm, I could store blogId with single element map and query for it like in example, but in that case each blogId will require individual index for it. Currently I have 300 blogs in my database, but what if I have few thousands? :D Even if I will store it in map with integer value instead of boolean, I can query only with one field, so it's impossible to invoke this: .whereGreaterThan("blogIdMap.kqy04zya72zit5x7n9", 0) .whereGreaterThan("blogIdMap.kqy0n2dt2maqluvi3eu", 10) – Skye Oct 13 '17 at 10:02
  • Yes, this seems to be a problem. Could you join to a table which has those IDs? – Tim Biegeleisen Oct 13 '17 at 10:05
  • Nope, Currently I'm storing blogId in post entity as normal field (blogId = 123131). But, users have map of subscribed blogs (123131 = true). I'm thinking about merging query observables into one, but it's not good idea, because I want to get f.e. 10 latest posts. And I will loose pagination ability. – Skye Oct 13 '17 at 10:20
  • @Skye I gave you an upvote, which is as much as I can do. Anyway for small ranges my answer might actually be viable, so I will leave it up. – Tim Biegeleisen Oct 13 '17 at 10:21
  • @TimBiegeleisen Does this works? Because from what I read, when using range comparison, you can use orderBy only on the key you applied comparision. In this case, it is "blogId" only. So orderBy on "timestamp" will not work. Can you please confirm if it's working? – Sandip Fichadiya Dec 26 '17 at 09:56
  • @SandipSoni I can't test Firebase at the moment. – Tim Biegeleisen Dec 26 '17 at 10:02
  • @TimBiegeleisen I didn't meant that :) I meant if you remember that was working for you when you used it with Firestore. – Sandip Fichadiya Dec 26 '17 at 10:04
  • This doesn't work because the Range filter and orderBy on different fields. Source: https://firebase.google.com/docs/firestore/query-data/order-limit-data – Ari May 20 '18 at 18:19
  • Note, firebase does now support IN query which works like OR for this purpose. See here: https://stackoverflow.com/a/59039919/2057171 – Albert Renshaw Nov 25 '19 at 20:56
1

Firestore has no OR queries. They way you'd do that is by running two queries and splicing them.

You could for example:

const search = ( key, value ) =>  collection( 'users' ).where( key, '==', value ).get().then( doc => doc.data() )

Promise.all( [ search( 'name', 'John' ), search( 'name', 'Johnny' ) ] )
.then( ( [ one, two ] ) => one.concat( two ) )
.then( allResults => dothings() )
Mentor
  • 856
  • 9
  • 17
1

For Android, I am using OR query like this.

List<String> values = new ArrayList<>();
    values.add("Value 1");
    values.add("Value 2");

and then use the .whereIn query like this.

db.collection("collectionName")
            .whereIn("fieldName", values)   // where values is the list we created
            .get()
            .addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
                @Override
                public void onComplete(@NonNull Task<QuerySnapshot> task) {

                    if (task.isSuccessful()) {

                      // Do your stuff

                    }

                }
            });
Aamir Ali
  • 203
  • 1
  • 8
0

Try this

collection("posts").start("blogId", "1")
.end("blogId", 2).orderBy("timestamp", Query.Direction.DESCENDING).limit(50)
varun
  • 4,234
  • 32
  • 28
-1

You can use De Morgaan's laws to rewrite an AND query as an OR query.

Teymour Aldridge
  • 1,546
  • 11
  • 25