How to execute a Group Collection Query using Firestore? (IOS) - ios

Hey everybody this is my first time asking a question on here so if the posting is incorrect etc... i apologize in advance.
I'm working on a project where students at the university I go to will be able to request a Resident Advisor to unlock their rooms for them. I'm stuck on how to query the entire(root) collection to find a subcollection with a field that contains a value that matches my collection ID. I've seen a lot of resources on Stack, Firebase, and i've tried to implement them but I've had no succes.
Here's a picture:
Accessing the Subcollection
My code snippet is:
db.collectionGroup("Dorms").whereField("UID", isEqualTo: UID).getDocuments { (snapshot, error) in
// here is where i'd like to gather the fields subcollection/document and then store them as variables
Thank you in advance for any help and advice. It's greatly appreciated!

This is my first time posting an answer so I hope this is somewhat helpful, but in my experience when querying files in a collection I create a forloop then use if let statements to get variables from the document
var someVariable : Int
db.collectionGroup("Dorms").whereField("UID", isEqualTo: "UID").getDocuments { (snapshot, error) in
if let e = error {
//this is printing the error if there is one getting the documents
print("There was an error getting the documents \(e)")
} else {
if let dorms = snapshot?.documents { //accesses all the documents in the collection
for doc in dorms {
let data = doc.data() //Gets all the information in the document
//Here you would use an if let to create variables from the information in the data
//For example
if let dormRoomNumber = data["dormRoomNumber"]/*You'll put the name of your field in here, so whatever you have named it in firestore*/] as? Int //make sure the the data type here mathces the data type in your firestore database
{
someVariable = dormRoomNumber
}
}
}
}
}

Related

How do I retrieve data from Firebase and return a boolean value accurately? [duplicate]

This question already has answers here:
Storing asynchronous Cloud Firestore query results in Swift
(1 answer)
How do I save data from cloud firestore to a variable in swift?
(1 answer)
How can I change the order of functions triggered?
(1 answer)
Closed 1 year ago.
I am having trouble figuring out how to modify my code to take into account the fact that Firebase runs asynchronously.
func checkIfFriends(_ SearchUserUID: String) -> Bool {
var friendArray: [String]?
currentUserDetails.getDocument { (document, error) in
if let document = document, document.exists {
friendArray = document.data()!["Friends"] as? [String]
}
}
if let friends = friendArray {
return friends.contains(SearchUserUID)
}
return false
}
currentUserDetails is the document of the current user that stores an array of friends that the current user has under the String, "Friends", where each element of the array is the UID of the friend. I would like to check if two users are friends by first retrieving the array of friends of the current user, and then checking if that array contains the UID of the friend we are searching for.
The issue is that
currentUserDetails.getDocument { (document, error) in
if let document = document, document.exists {
friendArray = document.data()!["Friends"] as? [String]
}
}
runs asynchronously so my friendArray is considered to be nil and my method always returns false. I am very confused as to how I can modify such methods to return values and take into account the asynchronous nature of Firebase data retrieval. Could someone help me out? Thank you very much!

How can I use the section parameter to iterate over each document that's inside of a collection to access the count of a subcollection?

So I'm working on my first workout tracking app and this is my first time using Firebase/Firestore, so I'm just trying to figure out if there is a simple query that I can use for this...
Here is what my Firestore Database structure looks like:
/Users/mi9P3TrLwkQ3oDIut/Days/WZ3Q6LDuu1kja/Workouts/BpLGFREoJNzNQW/Exercises/5vRWuHlcJHc/WeightReps/cKrB0Dpf0myEDQV0
Basically I need to return a value for numberOfRowsInSection, but the value that I need to access is the number of workouts that are associated with each day of the week, and I'm not too sure how to go about using the section parameter to iterate over each day document in my Days collection in order to access the Workouts subcollections and get the count of the documents there for each day collection. Does that make sense?
I hope that the question makes sense. Any help would be greatly appreciated!
Not entirely sure if I am getting your question right but if you want to retrieve several documents with all their attributes this is how you can do it:
var counter = 0
func getData() {
let db = Firestore.firestore()
let userID = Auth.auth().currentUser!.uid
for data in self.dataSourceArray {
db.collection("users").document(userID).collection("yourCollectionName").document(data.name).collection("yourCollectionName").getDocuments() { ( querySnapshot, error) in
if let error = error {
print(error.localizedDescription)
} else {
for document in querySnapshot!.documents {
self.counter += 1
}
}
}
}
}
Is this what youre looking for ?

Swift - Firestore Document references must have an even number of segments, but

Xcode is giving me this error message:
Document references must have an even number of segments, but users/hsiYfWgKz7MVOM77gMPkQYBHYJH2/wishlists has 3'
I have no idea why that error occurs because I thought there are 4 segments with this code:
let wishListName = self.popUpView.popUpTextField.text!
let db = Firestore.firestore()
let userID = Auth.auth().currentUser!.uid
db.collection("users").document(userID).collection("wishlists").document(wishListName).setData(["name": wishListName]) { (error) in
if error != nil {
print("Error saving Wishlist")
}
}
Note: The document(wishListName) does not exist before. I am trying to auto save it with the specific ID wishListName. Every help ist appreciated :)
This typically happens when userID or wishListName is empty, as you end up with only 3 segments in that case.
If you log these values right before the line of code that throws an error, you can find out which one of the two is empty.

How to structure a search list in Firestore?

I want to show a list of artists in my app which the user will be able to search through. I'm not sure however how to save this in Firestore?
First I created a collection "searchLists" with a document for each DJ but that means a lot of document reads so that's out of the question.
Now I created a document called "artists" which has a field "artistsDictionary" which contains all the artists.
| searchLists (collection)
* artists (document)
- artistsArray (array)
0: (map)
name: "Artist 0" (string)
1: (map)
name: "Artist 1" (string)
2: (map)
name: "Artist 2" (string)
And I retrieve and parse the array as followed:
let docRef = db.collection("searchLists").document("artists")
docRef.getDocument { (document, error) in
if let document = document, document.exists {
guard let documentData = document.data() else { return }
let artistsDictionaryArray = documentData["artistsArray"] as? [[String: Any]] ?? []
let parsedArtists = artistsDictionaryArray.compactMap {
return SimpleArtist(dictionary: $0)
}
self.artistsArray = parsedArtists
} else {
print("Document does not exist")
}
}
(SimpleArtist is a struct containing a "name" field.)
And I mean, it works, but I'm still new to Firestore and this seems kinda off. Is it? Or is this how I should/could do it?
First I created a collection "searchLists" with a document for each DJ but that means a lot of document reads so that's out of the question.
This is the right approach, so you should go ahead with it.
Why do I say that?
According to the official documentation regarding modeling data in a Cloud Firestore database:
Cloud Firestore is optimized for storing large collections of small documents.
Storing data in an array is not a bad option but this is most likely used, let's say to store favorite djs. I say that because the documents have limits in Firestore. So there are some limits when it comes to how much data you can put into a document. According to the official documentation regarding usage and limits:
Maximum size for a document: 1 MiB (1,048,576 bytes)
As you can see, you are limited to 1 MiB total of data in a single document. When we are talking about storing text, you can store pretty much but as your array getts bigger, be careful about this limitation.
First off, Alexs' answer is 100% correct.
I want to add some additional data points that may help you in the long run.
The first item is arrays. Arrays are very challenging in NoSQL databases - while they provide a logical sequence data via the index, 0, 1, 2 they don't behave like an array in code - so for example; Suppose you wanted to insert an item at an index. Well - you can't (*you can but it's not just a simple 'insert' call). Also, you can't target array elements in queries which limits their usefulness. The smallest unit of change in a Firestore array field is the entire field - smaller changes to individual elements of a field can't be made. The fix is to not use arrays and to let FireStore create the documentID's for you data 'objects' on the fly e.g. the 'keys' to the node
The second issue - (which may not be an issue currently) is how the data is being handled. Suppose you release your app and a user has 2 million artists in their collection - with your code as is, all of that data is downloaded at one time which will probably not be the best UI experience but additionally, it could overwhelm the memory of the device. So working in 'chunks' of data it a lot easier on the device, and the user.
So I put together some sample code to help with that.
First a class to store your Artist data in. Just keeps track of the documentID and the artist name.
class ArtistClass {
var docId = ""
var name = ""
init(aDocId: String, aName: String) {
self.docId = aDocId
self.name = aName
}
}
and a class array to keep the artists in. This would be a potential dataSource for a tableView
var artistArray = [ArtistClass]()
This is to write an artist as a document instead of in an array. The documentID is a FireStore generated 'key' that's created for each artist.
func writeArtists() {
let artistsRef = self.db.collection("artists")
let floyd = [
"name": "Pink Floyd"
]
let zep = [
"name": "Led Zeppelin"
]
let who = [
"name": "The Who"
]
artistsRef.addDocument(data: floyd)
artistsRef.addDocument(data: zep)
artistsRef.addDocument(data: who)
}
and then function to read in all artists.
func readArtists() {
let artistsRef = self.db.collection("artists")
artistsRef.getDocuments() { (querySnapshot, err) in
if let err = err {
print("Error getting documents: \(err)")
} else {
for document in querySnapshot!.documents {
let docId = document.documentID
let name = document.get("name") as! String
let artist = ArtistClass(aDocId: docId, aName: name)
self.artistArray.append(artist)
}
for a in self.artistArray { //prints the artists to console
print(a.docId, a.name)
}
}
}
}
So your data in Firestore looks like this
artists (collection)
8lok0a0ksodPSSKS
name: "Let Zeppelin"
WKkookokopkdokas
name: "The Who"
uh99jkjekkkokoks
name: "Pink Floyd"
so then the cool part. Suppose you have a tableView that shows 10 artists at a time with a down button to see the next 10. Make this change
let artistsRef = self.db.collection("artists").order(by: "name").limit(to: 10)
Oh - and you'll notice the function of sorting now goes the server instead of the device - so if there's a million artists, it's sorted on the server before being delivered to the device which will be significantly faster.
You can also then more easily perform queries for specific artist data and you won't need to be as concerned about storage as each artist is their own document instead of all artists in one.
Hope that helps!

Swift Query Into Firebase Firestore Nested Array

I have nested data that I'd like to display in a tableView.
My data is structured like so...
/users
/userid
name: "John"
age: 23
/likedPosts
0:post1
1:post2
For the tableview I'd like to display these posts (which have their own collection of data).
In order to do that I need to...
1) Get the count of the array and
2) Query the users likedPost array values to get the content of the post.
I'm currently using the getDocument function and can't figure it out.
for example...
func getUserLikedPosts() {
if let user = Auth.auth().currentUser {
let userFS = Firestore.firestore().collection("users").document(user.uid)
userFS.getDocument(completion: { (snapshot, error) in
print(snapshot?)
})
}
}
This doesn't even print out the nested array?
Something like this should work:
func getUserLikedPosts() {
if let user = Auth.auth().currentUser {
let userFS = Firestore.firestore().collection("users").document(user.uid).collection(“likedPosts”)
userFS.getDocuments(completion: { (snapshot, error) in
print(snapshot?)
})
}
}
With Firestore you need to drill down to the actual node you want, unlike Firebase where you can access child snapshots.
So after a quick discussion, with the OP, it was that the user in question didn’t have a likedPost object.
To access the array (Please be aware I haven’t tested this code, it is an example):
If let doc = document, let array = doc[“likedPost] as NSArray {
print(array)
}

Resources