Vulps
Vulps

Reputation: 238

Get Firestore document with OR query

I am using Firestore to store a Friend List for my iOS app, which is using Swift.

each document in my friends collection has a sender, target, and accepted field

when the user opens their friend list I need the app to retrieve every document where their user ID is in either the target or sender fields, and only where accepted == true

I know I can chain .where() functions to create AND queries.

but as far as I can see it's not possible to do OR queries with Firestore.

Is there a way to do this in one query?

Thanks

EDIT

Here is a snapshot of the database structure

enter image description here

Each document is created when a friend request is sent and is then used to build the friend list for both users when required (rather than storing 2 arrays of user IDs which have to be updated separately whenever a change is made)

Upvotes: 1

Views: 884

Answers (3)

Frank van Puffelen
Frank van Puffelen

Reputation: 599926

Update: Since early 2024 it is possible to have a query with OR conditions across multiple fields in Firestore. To learn how to create such a query and its limitations, see the Firestore documentation on queries, specifically the section on OR queries and limitations on OR queries.

For simplicity I'd probably still opt for the array membership solution that I recommended before though, so I'll leave that below for completeness:

I need the app to retrieve every document where their user ID is in either the target or sender fields

While Firestore now allows OR conditions between fields, I'd recommend an approach where your data model more closely matches the needs of your app.

If you add an array field to the document where you keep the UIDs of all participants, you can then check whether the UID you're looking for is in that array with an in operation:

friendsRef
  .whereField("participants", arrayContains: "uidOfUser")

This is essentially a more generic version of the OR-across-fields that you wanted to do, and it will likely allow more use-cases - at the cost of adding extra data to your documents. This trade-off of adding more data to (efficiently) allow a use-case is quite common when using NoSQL databases. If you're new to this, I recommend reading NoSQL data modeling techniques and watching Get to know Cloud Firestore.

Upvotes: 2

lambinator
lambinator

Reputation: 11029

Update: firestore (as of May 2023?) now allows for OR queries!

https://firebase.google.com/docs/firestore/query-data/queries#or_queries

let query = db.collection("cities").whereFilter(Filter.orFilter([
                Filter.whereField("capital", isEqualTo: true),
                Filter.whereField("population", isGreaterThanOrEqualTo: 1000000);
            ]))

Upvotes: 1

Jay
Jay

Reputation: 35667

See Franks correct answer, going outside the box a bit with arrays presents a solution. I wanted to show the structure we use to handle these cases (based on Franks answer)

Firebase can check for values in an array, so simply changing the structure allows an "or" style query. Here's some sample data

friends
   doc_0
      accepted: true
      requests //an array
         0: uid_0
         1: uid_2
   doc_1
      accepted: true
      requests
         0: uid_6
         1: uid_0
   doc_2
      accepted: true
      requests
         0: uid_1
         1: uid_6

Keep in mind that with this structure, array element 0 is the "sender" and array element 1 is the "target"

In this case suppose user 0 (uid_0) wants all of the docs where their users uid (uid_0) is either in the "sender" or "target" position, which would be doc_0 or doc_1. Here's the code

func getRequests() {
    let friendsCollection = self.db.collection("friends") //self.db points to my firebase
    let query = friendsCollection.whereField("accepted", isEqualTo: true)
                                 .whereField("requests", arrayContains: "uid_0")
    query.getDocuments(completion: { snapshot, error in
        if let err = error {
            print(err.localizedDescription)
            return
        }

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

        for doc in docs {
            let docId = doc.documentID
            print(docId)
        }
    })
}

And the result

doc_0
doc_1

Upvotes: 1

Related Questions