Swift Firebase query child of child [duplicate] - swift

This question already has answers here:
Firebase Query Double Nested
(3 answers)
Closed 5 years ago.
So I'm stuck for half a day here, I'm trying to get all the subjects of Arya but I'm having a hard time doing it.
Here's the sample structure in Firebase.
-Subjects
-math
id: 1
name: Math
-students
-Arya
id: 1
name: Arya
-JonSnow
id: 2
name: JonSnow
+justsomename
+science
+english
+history
+computer
Then I found this one Querying in Firebase by child of child.
I tried this
ref.queryOrdered(byChild: "students/name").queryEqual(toValue: "Arya").observeSingleEvent(of: .value, with: { snapshot in
print(snapshot)
})
ref == Subjects
but this is the return
Snap (subjects) <null>
Is the query correct and I'm just doing something wrong?

Maybe something like this.... could work...
var subjects = [String]()
ref.observeSingleEvent(of: .value, with: { snapshot in
let value = snapshot.value as? NSDictionary
let postsIds = value?.allKeys as! [String] // this will put all the subjects such as math into an array called postIDs
for postId in postsIds { //this is going to cycle through the array and check each one for your student
let refToPost = Database.database().reference(withPath: "Subjects" + postId)
refToPost.observeSingleEvent(of: .value, with: { snapshot in
if snapshot.hasChild("Arya") {
self.subjects.append(snapshot)
}
})
}
})
print("this is the ist of subjects with my student: \(subjects)")

Related

Check and compare messages in Swift and Firebase

How do I compare the responses of two people and calculate the percentage of similarity? For example, in my database below, Kevin answered questionId with "yes" and questionId1 with "no". John answered "yes" and "yes". I would like the output to show 50% given that they have the same answer for 1 and a different answer for the other.
I'm trying this but not sure how to compare other users:
func calculatePercentage(completion: #escaping ([String])->()) {
let postRef = self.databaseRef.child("responses").child("Kevin")
postRef.observeSingleEvent(of: .value, with: { (snapshot) in
var userIdArray = [String]()
for topic in snapshot.children.allObjects as! [DataSnapshot] {
let question1 = topic.childSnapshot(forPath: "questionId").value
let question2 = topic.childSnapshot(forPath: "questionId1").value
userIdArray.append(topic as! String)
}
completion(userIdArray)
})
}
fetch all responses that you want to compare as Dictionary or Arrays or Objects.
For more information and example please see firebase read_data document.
Compare each questions, it's same or not. then count the numberOfSameAnswers.
Like Joakim Danielson wrote, calculation is
percentageOfSameAnswer = (numberOfSameAnswers/numberOfAllQuestions)*100

Querying Firebase Data by Child

I am now long working with swift and Firebase but face the issue that one of my queries doesn't work properly.
Each of my messages has a creation Date assigned and I can call it from the data when I load the messages but I am not able to load it by the child "creationDate" which is my node for the timestamp.
My message is sent like in a chat app and there is no issue, following is my code for querying the data.
As I mentioned the path is correct and the code is just for testing purposes
Note : this is just a code snippet in order to test if the path is correct
func checkMessages() {
guard let currentUser = API.User.CURRENT_USER?.uid else {return}
API.Message.REF_MESSAGES_CHAT.child(currentUser).queryOrdered(byChild: "creationDate").observeSingleEvent(of: .value) { (snapshot) in
if let dict = snapshot.value as? [String:Any] {
for key in dict.keys {
if let value = dict[key] as? [String:Any] {
let creationDate = value["creationDate"] as! Double
let text = value["text"] as! String
print("creation:" , creationDate)
print("text:", text)
}
}
}
}
}
the print out would be
creation: 1514041174.13297
text: 8
creation: 1514041177.54951
text: 10
creation: 1514041171.24212
text: 6
creation: 1514041172.35241
text: 7
creation: 1514041168.06832
text: 3
creation: 1514041166.98511
text: 2
creation: 1514041170.10783
text: 5
creation: 1514041165.88657
text: 1
creation: 1514041169.09632
text: 4
creation: 1514041175.60879
text: 9
I sent them ordered properly ( 1,2,3,4,5,6,7,8,9,10).
Any idea?
I am really struggling at this point because I have this feature implemented over 30 times in my app and nowhere else its an issue just in querying this.
Edit: upload of a message
let creationDate = Date().timeIntervalSince1970
let timestamp = ["creationDate": creationDate, "text": text] as [String : Any]
API.Message.REF_MESSAGES_CHAT.child(currentUser).child(newMessageID).setValue(timestamp)
API.Message.REF_MESSAGES_CHAT.child(currentUser).queryOrdered(byChild: "creationDate").observeSingleEvent(of: .value) { snapshot in ... }

Crash while retrieving data from Firebase [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 5 years ago.
Improve this question
My Code
let ref = Database.database().reference().child("PostInfo")
let query = ref.queryOrdered(byChild: "post_title").queryEqual(toValue: self.retrieve_title)
query.observeSingleEvent(of: .value) { (snapshot) in
print((snapshot.childSnapshot(forPath: "status").value as? String)!)
}
}
jSon Data
{"PostInfo":{
"-KyjkkEAZeHLjdRLg20w" : {
"postImage" : "https://firebasestorage.googleapis.com/v0/b/hobbyquest-ee18d.appspot.com/o/8C40BA04-6D8D-4A23-B8BB-E1B3AC64E66F.png?alt=media&token=3f0f10e3-a64b-4187-9259-3c25bfc4a9e5",
"post_title" : "hahahahah",
"status" : "a banana an",
"threadForHobby" : "Bowling",
"userID" : "ccuvHt6feYVIO6GUXKo3OpO6VUn2"}
}
I am trying to get the status data from firebase but the app keeps crashing. Please help!
The problem is this line
(snapshot.childSnapshot(forPath: "status").value as? String)!
Your code is reading the data by .value.
.value returns ALL nodes within the given node and you will need to iterate over them. For example. Suppose our database looks like the following and you are querying for posts with a post_title of title_0
PostInfo
post_0
postImage = "www.thing.com"
status = "status_0"
post_title = "title_0"
post_1
postImage = "www.yipee.com"
status = "status_1"
post_title = "title_1"
post_2
postImage = "www.dude.com"
status = "status_2"
post_title = "title_0"
When running your query, both post_0 and post_2 will be returned because they both have title_0
You would need to iterate over the snapshot to get the results.
let ref = self.ref.child("PostInfo")
let query = ref.queryOrdered(byChild: "post_title").queryEqual(toValue: "title_0")
query.observeSingleEvent(of: .value) { (snapshot) in
for child in snapshot.children {
let snap = child as! DataSnapshot
let x = (snap.childSnapshot(forPath: "status").value as? String)!
print(x)
}
}
If you notice - your line of code works in this scenario because it's examining each child as a separate snapshot.
On the other hand, if you only want to return the first match, you can use .childAdded, which will return just an individual node:
let ref = self.ref.child("PostInfo")
let query = ref.queryOrdered(byChild: "post_title").queryEqual(toValue: "title_0")
query.observeSingleEvent(of: .childAdded) { (snapshot) in
let x = (snapshot.childSnapshot(forPath: "status").value as? String)!
print(x)
}
I don't know why but you could do it a different way but changing the layout of your data.
The data would be like this:
{"PostInfo":{
"searchidentifier" : { //whatever identifier you want, I think you're trying to use the post title
"object" : {
"postImage" : "https://firebasestorage.googleapis.com/v0/b/hobbyquest-ee18d.appspot.com/o/8C40BA04-6D8D-4A23-B8BB-E1B3AC64E66F.png?alt=media&token=3f0f10e3-a64b-4187-9259-3c25bfc4a9e5",
"post_title" : "hahahahah",
"status" : "a banana an",
"threadForHobby" : "Bowling",
"userID" : "ccuvHt6feYVIO6GUXKo3OpO6VUn2"}
}
}
And you would retrieve your data like this:
let ref = Database.database().reference().child("PostInfo").child("\(self.retrieve_title)")
ref.observe(.value, with: { (snapshot) in
if snapshot.childrenCount > 0 {
for classes in snapshot.children.allObjects as![FIRDataSnapshot] {
let classesObject = classes.value as? [String: AnyObject]
let postImage = classesObject?["postimage"]
//Retrieve all the other objects here as well
}
}
})

swift firebase nested children count

groups
--group1(autoid)
---subgroup1(autoid)
----id
----ownerId
----description
--group2(autoid)
---subgroup2(autoid)
----id
----ownerId
----description
In a structure like over here i have to count all occurrencies of ownerId that are equal to my id (currentUserId) in all groups, can somebody help me?
what i've done so far:
root.child("groups").observe(.value, with: {(snapshot) in
if let result = snapshot.children.allObjects as? [DataSnapshot] {
var count = 0
for child in result {
let orderID = child.key as String //get autoID
self.root.child("groups/\(orderID)/").queryOrdered(byChild: "ownerId").queryEqual(toValue: self.currentUserId).observe(.value, with: { (snapshot: DataSnapshot!) in
print(snapshot.childrenCount, "quanti sono")
count += (Int(snapshot.childrenCount))
print(count)
})
}
}
})
with this i can get a count but it updates all cycles... i need i need the final value outside
One important aspect of Firebase Structures is denormalizing or flattening the structure. Denormalized data generally makes queries much easier and while conceptually the structure you are using works for some tasks, it makes doing the query you want challenging.
So, I would suggest an alternate structure that would make the query super simple, and not loose other functionality.
A change to the structure like this:
groups
group1: true
group2: true
subgroups
subgroup1(autoid)
id
ownerId
description
belongs_to_group: "group1"
subgroup2(autoid)
id
ownerId
description
belongs_to_group: "group2"
Then if you want to count all of subgroups with a particular ownerId
let subGroupsRef = self.ref.child("subgroups")
let query = subGroupsRef.queryOrdered(byChild: "ownerId").queryEqual(toValue: "their id")
query.observeSingleEvent(of: .value) { snapshot in
let count = snapshot.childrenCount
print(count)
}
Edit:
Based on the comment, here's an way to get the count based on your current structure. It's pretty brute force and the code could be reduced considerably but I left it verbose for readability
let groupsRef = self.ref.child("groups")
groupsRef.observeSingleEvent(of: .value, with: { snapshot in
var count = 0
for groupChild in snapshot.children {
let groupSnap = groupChild as! DataSnapshot
for subGroupChild in groupSnap.children {
let subGroupSnap = subGroupChild as! DataSnapshot
let dict = subGroupSnap.value as! [String: Any]
let uid = dict["owner_id"] as! String
if uid == "uid_0" {
count += 1
print(count)
}
}
}
print("total found \(count)")
})
Where this fails is if you have a lot of nodes as they are all initially loaded in (by .value) so it could be iterated over in code. If it's a few thousand it works well and is very fast.
You can achieve this step by doing a single observe of .childAdded type. The .childAdded on an sub child, in your example groups, is like a for loop that iterate all nodes.
With this configuration you can append a .queryOrdered(byChild:) and a .queryEqual(toValue:):
ref.child("groups")
.queryOrdered(byChild: "ownerID")
.queryEqual(toValue: 123)
.observe(.childAdded)
{ (snap) in
print(snap)
}
After that, if you want to count all this child, you need to add a property on your class
This is a test example:
To optimize performance remember to add a .indexOn rule on your firebase app:
"groups" : {
".indexOn" : "ownerID"
}
Hope this help you ;)

Firebase/Swift queryOrder then match value

I asked a question yesterday that was marked as a duplicate, and when I updated the question it was not unmarked. So I am asking again here (as per stackoverflow's recommendation).
I am trying to sort by multiple values in firebase. I understand that is not possible, but i was given an example in another language which is only half helpful as how to go about doing it the right way. In any case i tried to follow the example given here Query based on multiple where clauses in firebase .
This is the structure of my firebase
room
-KJe22sduQMz1DIs_DH6
allowedParticipants:
14
createdBy:
"Mr Tester"
members:
"nmeMYnnSatRch5qKPJKIe7jEOLy2"
participating:
true
status:
"seedling"
theme:
"Cats"
totalNumberOfMembers:
1
and this is the code that I am trying to get to work
ref.queryOrderedByChild("status").queryStartingAtValue("active").queryEndingAtValue("active").observeEventType(.Value) { (snapshot: FIRDataSnapshot) in
let themeOfEvent = snapshot.value
if themeOfEvent?.value == pickedTheme {
print("foo")
}
}
Could somebody please post a useful comment or answer to help me?
Thank you
I was able to get help
This works
func listOfPossibleCompetitionsFromFirebase(){
let createdRoomRef = firebase.child("room")
createdRoomRef.queryOrderedByChild("status").queryStartingAtValue("active").queryEndingAtValue("active").observeEventType(.Value) { (snapshot: FIRDataSnapshot) in
var themeCount = 0
self.listOfOpenComps.removeAll()
if let tmp = snapshot.value as? [String:AnyObject] {
let keys = tmp.keys
for key in keys {
if let roomDetails = (tmp[key] as? [String:AnyObject]) {
if let themeOfEvent = roomDetails["theme"] as? String where themeOfEvent == pickedTheme {
themeCount += 1
self.listOfOpenComps.append(key)
}
}
}
}
dispatch_async(dispatch_get_main_queue(), {
self.tableView.reloadData()
})
print("rooms count: \(themeCount)")
}
}