Reputation: 238
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
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
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
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
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