Firebase retrieving child nested data - ios

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

Related

retrieve data from firebase and set VC to match users preferences

I have a viewController that is very similar to tinder. A user can swipe left or right depending on how many values are stored in firebase. Every value from firebase shows on the users VC no matter if it matches their preferences or not. I wanted to allow users to have preferences (again, very similar to tinder.) and only values that fall within those limits to show on the viewController. The way I am fetching all of the values from firebase is
func fetchPost() {
topCardView = nil
var previousCardView: CardView?
Database.database().reference().child("Post").observe(.childAdded, with: { (snapshot) in
if let userDictionary = snapshot.value as? [String: AnyObject] {
let poster = Poster(dictionary: userDictionary as [String : AnyObject])
let cardView = self.setupCardFromUser(poster: poster)
self.cardViewModels.append(poster.toCardViewModel())
if let currentUser = Auth.auth().currentUser?.uid,
currentUser == poster.fromId {
cardView.removeFromSuperview()
} else if self.topCardView == nil {
self.topCardView = cardView
}
previousCardView?.nextCardView = cardView
previousCardView = cardView
}
}, withCancel: nil)
}
The code above, allows the user to see every single value from firebase. but the preferences I want to manipulate this is cost and skills.
is there a simple way for me to only show the values from firebase if they match the users preferences?
in firebase, the usersPreferences tree is set up as
users
|______ usersUID
|______ minSeekingCost
|______ maxSeekingCost
|______ skills1
and how the postings are set up look like
post
|____ usersUID
|____ category
|____ cost
I want the users to find postings that are within the min&maxSeekingCost, and match their skills. Say if a post matches one of their skills, and the price is not within their limits, then it is not fetched. Same for if post does not match and the price is in their limits.
would I have to fetch the users preferences inside of the fetchPost? or can I manipulate the fetchPost itself to have these called.
You're looking for queries, which allow you to order and filter data.
But Firebase Database queries can only order/filter on a single property. In certain cases it is possible to combine the values you want to filter on into a single (synthetic) property. For an example of this and other approaches, see my answer here: Query based on multiple where clauses in Firebase

How to Sort Firebase SnapShot by inner Child

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)
}
})

Firebase: what is a FirDatabaseQuery object actually used for?

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.

Firebase sorting clarification on snapshot.value

Was hoping someone could help me understand Firebase snapshot and sorting. Why if you queryOrderedByChild on a FirebaseDBRef, the snapshot.value returns as displayed on the Firebase Database (using the browser), but if you do for snap in snapshot.children{} the values do show sorted. Just wanting clarification on why this happens.
Let me explain with example.
let dinoRef = rootRef.child("dino")
let query = dinoRef.queryOrderedByChild("dimensions/height")
query.observeSingleEventOfType(.Value, withBlock: { snapshot in
// This prints to the console unsorted
print(snapshot.value)
// This prints to console sorted
for snap in snapshot.children {
print(snap)
}
})
Thank you.
A FIRDataSnapshot contains information about the keys of the returns data, the values associated with those keys and the order of the keys. When you call snapshot.children the snapshot the enumerator will return the children in the order you requested.
When you call snapshot.value on a snapshot, it must convert all the information to an NSDictionary. A dictionary has no inherent information on the order of data, so the order of the results is lost at this point.
You must try order by child with ascending and descending option. That will clarify your doubt.

Add value instead of change value In Firebase with Swift

I would like to save and retrieve features to and from Firebase into a TableView.
The child I would like to save them under is the uid (unique user id)
so a feature would look like this in the database:
Firebase database
The ideal situation, is how the "derde" is saved, so the uid as a key and "derde" as the value.
#IBAction func saveButtonPressed(sender: AnyObject) {
let featureContents = addFeatureTextField.text
if featureContents != "" {
// Build the new Feature.
let newFeature: String = featureContents!
let ref = DataService.dataService.FEATURE_REF.childByAppendingPath(uid)
ref.setValue(newFeature)
where uid is a String, retrieved from authdata somewhere else in the code.
If I save it like this, it saves it to the specific uid path. If I want to add another feature by clicking on the + in the TableViewController, it saves it to the same path, so the Firebase database is updated with the new value and so instead of two features you only end up with one updated feature.
You can prevent this by working with the chilByAutoId() method, to save a list of items. The code would look like this:
#IBAction func saveButtonPressed(sender: AnyObject) {
let featureContents = addFeatureTextField.text
if featureContents != "" {
// Build the new Feature.
let newFeature: String = featureContents!
let ref = DataService.dataService.FEATURE_REF.childByAutoId().childByAppendingPath(uid)
ref.setValue(newFeature)
via this way, a feature is saved, as you can see in the above image at: "vierde"
This allows you to save multiple features with all the same uid, but different autoId.
But, if I save it like this, my tableView stays empty. The TableViewController is like this:
DataService.dataService.FEATURE_REF.observeEventType(.Value, withBlock: { snapshot in
// The snapshot is a current look at our features data.
print("The features in the tableView should be \(snapshot.value)")
self.features = []
if let snapshots = snapshot.children.allObjects as? [FDataSnapshot] {
for snap in snapshots {
// Make our features array for the tableView.
if let postDictionary = snap.value as? String {
print("All in")
let key = snap.key
let feature = Feature(key: key, value: postDictionary)
// Items are returned chronologically, but it's more fun with the newest features first.
self.features.insert(feature, atIndex: 0)
}
}
}
// Be sure that the tableView updates when there is new data.
self.tableView.reloadData()
})
}
Problem lies in this code: if let postDictionary = snap.value as? String {
This conditional binding does not succeed, because the value is not a String, but the autoId key has no value, only the child under it which is the uid has a value "vierde"
Two possible solutions which I am asking you guys:
1) How can I save multiple features with the same uid without using the autoId?
2) If I am obliged to use the autoId, how can I make sure it observes the value of the uid key under the autoId, instead of the non existing value of the autoId.
Thanks for your help!
I think the answer to the question is to build a dictionary out of the key:value pairs of data and store that as a child of your uid node
let featureDict = [ "feature_0": "cool feature", "feature_1": "great feature"]
let ref = DataService.dataService.FEATURE_REF.childByAppendingPath(uid)
ref.setValue(featureDict)
results in
the_uid
feature_0: "cool feature"
feature_1: "great feature"
The limitation here is the key's names, and then the ability to add even more data about each feature.
Here's a potentially better option
the_uid
auto_id_0
feature_name: #"cool feature"
summary: "Everything you'd ever want to know about this feature"
auto_id_1
feature_name: #"great feature"
summary: "Info about this great feature"
The auto_id_x is generated by autoId and allows you to add however many features you want, change their names and summaries. etc. The children of each auto_id_x are (or could be) stored in a dictionary and saved per the above example.

Resources