This question isn't about how to loop over children, it's about looping over children in different ways to save costs
My database is:
-someRef
-someUserId // say this ref has 10000 children
-uid1: someVal
-uid2: someVal
-uid3: someVal
...
-uid7989: someVal
...
-uid10000: someVal
Following this answer I can loop over children like so:
var lookForThisUserId = "uid7989"
someRef?.observeSingleEvent(of: .value, with: { (snapshot) in
for child in snapshot.children {
let snap = child as! DataSnapshot
if snap.key == self.lookForThisUserId {
// user found
break
}
}
})
But I asked this question the other day and initially picked this answer which turned out to be an inefficient way of trying to do what I wanted done. In the comments #Jay said
"There's an accepted answer but here's the issue. What you're
essentially trying to do is track the number of children in a node. As
is, that's done by reading in the entire node (which could be
thousands of votes) and using .childrenCount. That will result in
reading in a LOT of unnecessary data and driving up costs"
So it got me to thinking, is there any difference in costs between the loop code I added above and this code below:
var lookForThisUserId = "uid7989"
someRef?.child(lookForThisUserId).observeSingleEvent(of: .value, with: { (snapshot) in
if !snapshot.exists() {
// this user isn't in here
}
// user found
})
Related
I want to retrieve data on with this value 7hmpcTuCAYQAYRqP7RNmnegSd9r2
But i'm getting all four objects in snapshot. I want to get parent key values which contain this key
7hmpcTuCAYQAYRqP7RNmnegSd9r2
Need to get blue mark keys.
Here is my code
let ref = FirebaseManager.refs.databaseRoot.child("new_ChatListMembers")
ref.queryOrdered(byChild: (Auth.auth().currentUser?.uid)!).queryEqual(toValue: true)
ref.observeSingleEvent(of: .value) { (snapshot) in
print(snapshot)
}
This code return four objects instead of two.Please help me how i can get specific data.
Thanks
You're not actually using the query that you construct in your code. To use that, it'd be something like:
let ref = FirebaseManager.refs.databaseRoot.child("new_ChatListMembers")
let query = ref.queryOrdered(byChild: (Auth.auth().currentUser?.uid)!).queryEqual(toValue: true)
query.observeSingleEvent(of: .value) { (snapshot) in
print(snapshot)
}
But this won't actually scale very well, as you'll need to define an index for each individual UID value in this structure. In short: your current data structure makes it each to find the users for a specific chat room, but it doesn't help finding the chat rooms for a specific user.
To allow the latter, you'll want to add an extra structure in your data:
user_chats: {
"$uid": {
"$chatid": true
}
}
So this is pretty much the inverse of what you have already, which is why this is often called an inverse index.
For more on this, and another example, see my answer here: Firebase query if child of child contains a value
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 3 years ago.
Improve this question
So I have the following code to retreive data from my firebase database:
ref = Database.database().reference().child("data").child("someData")
ref.observeSingleEvent(of: .value) { (snapshot) in
for child in snapshot.children {
let someSnap = child as! DataSnapshot
self.someArray.append(someSnap.key)
self.collectionView?.reloadData()
}
This code succesfully appends the 4 string elements i have in my database to the array in my viewcontroller class, however it does so over and over again. The array keeps growing with hundreds of strings of the same 4 items over and over. How can I get only the 4 items I want?
It seems that you call the function many times , so make sure to clear it
self.someArray.removeAll()
for child in snapshot.children {
let someSnap = child as! DataSnapshot
self.someArray.append(someSnap.key)
}
self.collectionView?.reloadData()
As from your question, you are calling function many times, when you call function again remove all elements from array or clear array and reload your collection view so it does not add same items again. As updated your code:
ref = Database.database().reference().child("data").child("someData")
ref.observeSingleEvent(of: .value) { (snapshot) in
self.someArray.removeAll()
for child in snapshot.children {
let someSnap = child as! DataSnapshot
self.someArray.append(someSnap.key)
}
self.collectionView?.reloadData()
}
Since I don't have access to the database I'd suggest you use the debugger over the for loop and trace it step by step and checking for the values in each iteration to see what exactly you're appending to the array
I have dictionaries in Firebase real-time database. I need to sort the entire dictionaries with an inner child in the dictionary.
I need to query the sort, using timestamp child.
self.ref?.child(FirebaseConstants.main.dbBase).child(getRoom(forUser: id)).queryOrdered(byChild: "timeStamp").observe(.value, with: { (snapShot) in
print(snapShot)
})
I tried this not working.
Since we don't know the value of the variables, it's hard to say precise. But say this structure is under /messages/10_22 then you can query it with:
Database.database().reference("messages/10_22").queryOrdered(byChild: "timeStamp").observe(.value, with: { (snapShot) in
println(snapshot.childrenCount)
for message in snapshot.children.allObjects as [FIRDataSnapshot] {
println(message.key+": "+message.value)
}
})
I'm making an app where users can buy and sell tickets. Users are able to create a new ticket and it successfully uploads to firebase however a reference to the ticket ID is stored in the user data which references the ticket id in the ticket data. The structure of the database is below:
DATABASE
USERS
TICKETS
TICKETS
TICKET INFO
USER
USER INFO AND TICKET ID OF TICKETS THEY ARE SELLING
My problem is that the first time I load the tickets from the selling tickets it's fine. However when the user adds a new ticket that they are selling, the table view loads everything twice.
override func viewWillAppear(_ animated: Bool) {
self.tickets = []
DataService.ds.REF_USER_CURRENT.child("selling").observe(.value, with: { (snapshot) in //HERE WE REFERNCE OUR SINGELTON CLASS AND OBSERVE CHAMGE TO THE POSTS OBJECT
self.tickets = [] //WE CLEAR THE POSTS ARRAY BEFORE WE START MANIPULATION TO MAKE SURE THAT WE DONT REPEAT CELLS
if let snapshot = snapshot.children.allObjects as? [DataSnapshot]{
print("ADAM: \(snapshot)")//CHECKING THAT THE OBJECTS EXIST AS AN ARRAY OF DATA SNAPSHOTS
for snap in snapshot {
DataService.ds.REF_TICKETS.child(snap.key).observe(.value, with: { (snapshot) in
if let ticketDict = snapshot.value as? Dictionary<String, AnyObject>{
let ticket = Ticket(ticketID: snap.key, ticketData: ticketDict)
self.self.tickets.append(ticket)
}
self.sell_ticketsTableView.reloadData()
})
}
}
//self.sell_ticketsTableView.reloadData()
self.tickets = []//RELAOD THE DATA
})
}
I'm not quite sure where I have gone wrong.
Please change your code to this. I have added the part where you clear your array
override func viewWillAppear(_ animated: Bool) {
self.removeAll() // This is how you clear your array
DataService.ds.REF_USER_CURRENT.child("selling").observe(.value, with: { (snapshot) in //HERE WE REFERNCE OUR SINGELTON CLASS AND OBSERVE CHAMGE TO THE POSTS OBJECT
self.tickets = [] //WE CLEAR THE POSTS ARRAY BEFORE WE START MANIPULATION TO MAKE SURE THAT WE DONT REPEAT CELLS
if let snapshot = snapshot.children.allObjects as? [DataSnapshot]{
print("ADAM: \(snapshot)")//CHECKING THAT THE OBJECTS EXIST AS AN ARRAY OF DATA SNAPSHOTS
for snap in snapshot {
DataService.ds.REF_TICKETS.child(snap.key).observe(.value, with: { (snapshot) in
if let ticketDict = snapshot.value as? Dictionary<String, AnyObject>{
let ticket = Ticket(ticketID: snap.key, ticketData: ticketDict)
self.tickets.append(ticket)
}
self.sell_ticketsTableView.reloadData()
})
}
}
//self.sell_ticketsTableView.reloadData()
})
}
You are observing the value of what a user is selling, which means every time they add something to this list, your listener will trigger and give you the new value of users/$uid/selling in its entirety.
This is why you are seeing double when the user adds another ticket; the listener is triggered and you append each ticket to the array again. You can get around this by checking if the ticket is already in the array before you append it however, your current implementation can be improved.
Instead of using observe(.value, you should use .childAdded. The listener will trigger every time a new child is added and only give you that specific child snapshot.
The listener will initially trigger for each child at that reference and you can append them to the array individually. However, once all the children have been loaded, the next child to be added will trigger this listener, which you can append to the array.
I've found millions of examples and tutorials on how to obtain a FirDatabaseQuery object (or DatabaseQuery as its now been renamed to) using one of the query....() methods.
But not one of these examples then goes on to show what you can do with the query once you have obtained it.
If you use one of the query methods to obtain a subset of the data as a DatabaseQuery, how do you actually access the set of data objects that it represents?
If you can't, for example, iterate through the query results like you can iterate through a snapshot, then what is a DatabaseQuery actually used for then?
I think that the answer to this is easily found in the docs.
I think that in the Android version of the class it is explained pretty well:
The Query class (and its subclass, DatabaseReference) are used for reading data. Listeners are attached, and they will be triggered when the corresponding data changes.
I can explain it in my own words and more practical like this:
You can use the DatabaseQuery object like a DatabaseReference just with the limitation that only the observe functions work and none of the methods used to manipulate the value, i.e. setValue() etc., of the reference nor any methods related to other nodes, i.e. parent and child (child() etc.), exist.
(I took the code from the docs, so please be free to optimize it/bring in your coding style)
This is how you get data from a DatabaseReference:
var postRef: DatabaseReference!
postRef = Database.database().reference().child("posts")
refHandle = postRef.observe(DataEventType.value, with: { (snapshot) in
let postDict = snapshot.value as? [String : AnyObject] ?? [:]
// ...
})
And this is how you get data from a DatabaseQuery:
// My top posts by number of stars
let myTopPostsQuery = (ref.child("user-
posts").child(getUid())).queryOrdered(byChild: "starCount")
refHandle = myTopPostsQuery.observe(DataEventType.value, with: { (snapshot) in
let postDict = snapshot.value as? [String : AnyObject] ?? [:]
// ...
})
As you can see the second part remains the same, as I mentioned, so in retrieving data you handle a DatabaseQuery like a DatabaseQuery. But I have to agree with you on the point that the iOS especially Swift docs are not that well made, the explanations for the other languages are way clearer.