How can I return a count of second level collection documents in Firebase? - swift

I am trying to provide a count of the number of items in the ‘Products’ collection for each user, and display this in a label on the UI of my app. This is how my Firebase database is structured:
Users
user 1
products
product 1
product 2
user 2
products
product 1
So for example, when user 1 logs in, it will display that they have 2 products, whereas when user 2 logs in, it will display that they have 1 product.
Below is my code which is used to return this data for each user.
document.reference.collection("products").get().then(function(querySnapshot) {
However, I am getting the below errors for this line of code.
Error 1 - “Cannot find 'querySnapshot' in scope”
Error 2 - “Cannot find 'function' in scope”
Here is my full method I am using to retrieve and display a count of items for the currently signed in user.
func databaseCount() {
let db = Firestore.firestore()
let user = Auth.auth().currentUser
db.collection("users").getDocuments { (snapshot, error) in
if let error = error {
print(error)
return
} else {
for document in snapshot!.documents {
let data = document.data()
let userId = data["uid"] as! String
if userId == user?.uid {
document.reference.collection("products").get().then(function(querySnapshot) {
console.log(querySnapshot.size);
let itemCount = data(querySnapshot.size) as! String
self.welcomeLabel.text = "Your item count is \(itemCount)!"
});
}
}
}
}
}
How would I go about updating my code to achieve my intended goal?

What i usually do in such scenarios is, When you create document in Users collection create an extra field of name cartItemCount with value 0 by default. In your case productsCount.
Users
---------user1
------------------name
------------------email
------------------productCount
------------------Products
--------------------------------product1
So when ever a document is added to Product collection of a User. Increment the value of productsCount by 1 and decrement by 1 if document is removed using the reference to that user document.
FieldValue.increment(Int64(1))
FieldValue.increment(Int64(-1))

Related

Swift: How do I repeat a getDoc request to Firestore?

I can already check if a doc exists in a collection. However I am unable to repeatedly check the same collection while trying different path names.
For example, my collection name is the UID of the user. There can be an unlimited amount of docs in this collection. The docs are titled "UID-0", "UID-1", "UID-2" and so on as the user adds items.
Every time it finds a doc that already exists such as "UID-0" it will change the path request to "UID-+=1" until the number exceeds the docs and it is able to create and use that path name.
Each doc contains about a dozen fields of the same data model but of course different data.
var docAlreadyExists: Bool = true
var multipleUserFencesIdCount: Int = 0
var newID: String = ""
let id = self.auth.currentUser?.uid ?? ""
repeat {
print("1")
self.fencesInfoCollection.document("Live").collection(id).document(newID).getDocument(completion: { document, error in
print("2")
if let document = document, document.exists {
print("EXISTS")
multipleUserFencesIdCount += 1
newID = newID.dropLast() + "\(multipleUserFencesIdCount)"
} else {
print("DOES NOT EXIST")
docAlreadyExists = false
}
})
} while docAlreadyExists
With that said, how can I repeatedly check if a document exists until the path name exceeds the rest and is able to create a new doc with the new data.
Edit:
The 1 gets repeatedly called correctly but the .getDoc never calls since 2 is never printed.
I figured out a better solution to my goal, instead of trying to repeat a call with different IDs I am now getting all documents and counting how many are in the collection.
self.fencesInfoCollection.document(id).collection("Live").getDocuments(completion: { document, error in
if let document = document, document.isEmpty {
print("EMPTY")
} else {
print("DOC1: \(String(describing: document?.count))")
}
})

How can I reference second level collections in Firebase?

Currently my Firebase database is using the following structure
Users
User 1 Data
User 2 Data...
However I have created a new collection inside of the "users" collection called "products" which will store product details that users have uploaded on the application.
How can I ensure that once a user uploads a new product, it is only uploaded into their 'User X data' dataset, inside the respective "products" collection. The code I currently have only uploads the data into the "users" collection, with no reference of the users who added the product as required. I have shown the structure of my Firebase database below for reference.
Here is my code:
let user = Auth.auth().currentUser
db.collection("users").getDocuments { (snapshot, error) in
if let error = error {
print(error)
return
} else {
for document in snapshot!.documents {
let data = document.data()
let userId = data["uid"] as! String
if userId == user?.uid {
db.collection("users").document("products").setData(["productNameField":firstName, "productURLField":lastName,"productPriceField":ebayName, "productDescriptionField":etsyName, "productTimeRemainingField":email])
}
}
}
}
How would I go about updating my code to achieve this?
I think you're looking for
let user = Auth.auth().currentUser
db.collection("users").getDocuments { (snapshot, error) in
if let error = error {
print(error)
return
} else {
for document in snapshot!.documents {
let data = document.data()
let userId = data["uid"] as! String
if userId == user?.uid {
document.reference.collection("products").addDocument(["productNameField":firstName, "productURLField":lastName,"productPriceField":ebayName, "productDescriptionField":etsyName, "productTimeRemainingField":email])
}
}
}
}
So here document is the DocumentSnapshot that you're looping over, so document.reference gives you the reference to that specific document, and document.reference.collection("products") then points to the `products subcollection for that specific document.
You're wastefully looping over the entire list of users to FIND the user document needed. Simplify, simplify - use the user.uid as the Id of the user document!! (i.e. when you create the user document, save it as
db.collection("users").doc(user.uid).set({whatever})
...then it's trivial to access as...
let user = Auth.auth().currentUser
db
.collection("users")
.doc(user.uid)
.collection("products")
.setData([
"productNameField":firstName,
"productURLField":lastName,
"productPriceField":ebayName,
"productDescriptionField":etsyName,
"productTimeRemainingField":email
]);
If there is another reason to keep the docID and the uid separate (not shown here), then use a query to get the SPECIFIC document with the uid, rather than downloading ALL of them
let user = Auth.auth().currentUser
db
.collection("users")
.where(field: "uid", opStr:"==", value: user.uid)
.getDocuments( { (snapshot, error) in
if let error = error {
print(error)
return
} else { //the query should only return the one document, but queries always return an array
snapshot
.documents[0]
.ref()
.document("products")
.setData([
"productNameField":firstName,
"productURLField":lastName,
"productPriceField":ebayName,
"productDescriptionField":etsyName,
"productTimeRemainingField":email
])
}
}
...etc...
I don't generally use Swift, so the where clause may require different formatting, but the idea is GET ONLY THE DOCUMENT YOU NEED. Your security rules should only be allowing this, anyway.

Firebase firestore getting data from inside collection

Is there a way to grab the data from owner ID I'm not able to access the collection oath because the documentID is auto-generated and not a specific value I have control of
A possible option would be to query your documents and retrieve a specifc OwnerID like this.
If you want to fetch all OwnerIDs from all of your documents just remove the 'whereField'.
Also check out the Firebase documentation for queries.
let docRef = db.collection("YOURCOLLECTIONNAME")
let query = docRef.whereField("OwnerID", isEqualTo:"VAL")
query.getDocuments{ (querySnapshot, error) in
if let error = error {
print("\(error.localizedDescription)")
}else {
for documents in (querySnapshot?.documents)! {
if let owner = documents.get("OWnerID") as? String {
print(owner)
}
}

How can I create a snapshot to listen to users being ADDED to my Firestore database?

I am using Firestore. I am trying to listen for when a new user is added. The problem is, each user also has a friends dictionary. So when I use a snapshot, my code is detecting both events of (1) A new user being added and (2) a new friend being added.
I have tries iterating over the document changes data and restricting doc.document.data()["friends"] == nil. Why isn't this working/how can I properly add a restriction to only include when a new user is added?
func observeUsers(onSuccess: #escaping(UserCompletion)) {
Ref().firestoreUserRef.collection("users").addSnapshotListener { (querySnapshot, error) in
if error != nil {
print("error with observeUser snapshot")
return
}
querySnapshot?.documentChanges.forEach { doc in
//I want to detect that a new user was added, I do not want to detect if a friend was added
if (doc.type == .added) && doc.document.data()["friends"] == nil {
guard let dict = querySnapshot else { return }
for document in dict.documents {
var dictionary = [String : Any]()
dictionary = document.data()
if let user = User.transformUser(dict: dictionary) {
onSuccess(user)
}
}
}
}
}
}
The easiest way is starting from the data model to support the types of queries you want. In this case, when you create a new user (which I assume always has no friends), set the friends field explicitly to null. This will let you query for all new users:
Ref().firestoreUserRef.collection("users").whereField("friends", isEqualTo: NSNull()).addSnapshotListener ...
An assumption here is your transformUser process will update the document and replace null with either a list of friends of an empty array so that it no longer matches the query.

Query to count number followers swift 4

I'm trying to count the number of followers the user has from my firebase database but no success. The number of followers is returning any value. When a user follows another user it creates a node in firebase with the userID and a Int value of 1 as shown below.
fileprivate func countTrusting() {
guard let userId = self.user?.uid else { return }
guard let currentLoggedInUser = Auth.auth().currentUser?.uid else { return }
Database.database().reference().child("following").child(userId).queryOrderedByValue().queryEqual(toValue: 1).observe(DataEventType.value, with: { (snapshot) in
let count = String(snapshot.childrenCount)
self.trustedLabel.text = "\(count) \nFollowing"
}) { (error) in
print("There was an error getting the following count:", error)
}
}
My firebase database
If I understand the question, you want to have a count of the number of followers of a user. In that case, there's no need for a query as the only nodes that will exist are the users followers.
This code reads in the child followers nodes and prints the count.
let userFollowingRef = fbRootRef.child("following").child("some_user_uid")
userFollowingRef.observeSingleEvent(of: .value, with: { snapshot in
print(snapshot.childrenCount)
})