Xcode Firebase query to fetch spacific data - ios

My data model
{
"channels":{
"channel_key_0":{
"creator":"pqr",
"name":"Channel-1",
"participant":{
"id_1":"Jack",
"id_2":"Harry"
}
},
"channel_key_1":{
"creator":"xyz",
"name":"Channel-2",
"participant":{
"id_3":"Jerry",
"id_2":"Harry"
}
}
}
}
From given above structure I want to fetch channels of only where participant has value "id_1": "Jack".
I am new to Firebase database. Need help to write this query. I tried few things FIRDatabase.database().reference().child("channels").queryOrdered(byChild: "participant").queryEqual(toValue: "Jack", childKey: "id_1"), but giving me null data.
Improvement in structure is also welcome. My idea is to build personal chat by applying this structure.

Try like this way.
let ref = FIRDatabase.database().reference().child("channels")
ref.queryOrdered(byChild: "participant/id_1").queryEqual(toValue: "Jack")
.observeSingleEvent(of: .value, with: { snapshot in
print(snapshot)
})

Related

Checking if database value is greater than local value in Firebase

I think this is simple but can't seem to create the correct query. I'm using Swift with Firebase Realtime Database on iOS.
Consider the following Firebase structure:
-DATA
--UID
---LEVEL1
----HIGHSCORE
I am trying to create a query to find out whether the specific HIGHSCORE number for a level is greater than a local variable. If it is, I want the return to be the higher value from Firebase. If it is the same or lower, I want the return to be null.
I have tried many different queries, here is an example:
ref.child("DATA/UID/LEVEL1/HIGHSCORE").queryStarting(atValue: 5000).observeSingleEvent(of: .value, with:
I know this is fundamentally incorrect, but I think it shows what I am trying to do.
Thanks in advance for any help.
Mike
EDIT: Here is the actual JSON example:
{
"DATA" : {
"USERID" : {
"LEVEL1" : {
"HIGHSCORE" : 5000,
}
}
}
}
The only way I can think of is with an orderByValue:
ref.child("DATA/UID/LEVEL1").orderByValue().queryStarting(atValue: 5000).observeSingleEvent(of: .value, with:
But this only works well if HIGHSCORE is the only child node under LEVEL1. Even then: it doesn't have any bandwidth or performance over the more idiomatic approach:
ref.child("DATA/UID/LEVEL1/HIGHSCORE").observeSingleEvent(of: .value, with: { (snapshot) in
if snapshot.exists() {
val score = snapshot.value as! Int
if score > 5000 {
...
}
}
})

How do I search for a particular item in my database using Swift and Firebase?

So I am building an app where I add certain words to my Firebase Database. When the user enters the word they want to add, I want to check if that word already exists in the database. If it does then I would show an alert. If not, I'll add it to the database. How would I do that with Swift? Any help would be appreciated. Thank you!
Here's the database structure:
speaktext-6-----
wordList
-LWQObIw1PKWJ_B9jNfp
word: "Water"
wordType: "Noun"
You need to take into account there may be a significant number of words so loading all of the words in will not only be slow as they have to loaded and then iterated over to find the word you are looking for, but it may also overwhelm the devices memory.
A simple solution is a Firebase Query - let Firebase do the heavy lifting and just return the node you want. It will be a LOT faster and won't overwhelm the device.
Here's a function that will tell you if a word exists within your firebase structure
func findWord(aWord: String) {
let ref = self.ref.child("wordList")
let query = ref.queryOrdered(byChild: "word").queryEqual(toValue: aWord)
query.observeSingleEvent(of: .value, with: { snapshot in
if snapshot.exists() {
print("found the word")
//you could expand on this to indicate it was a Noun etc
} else {
print("\(aWord) is not in the wordList")
}
})
}
Also review the Firebase Sorting and Filtering Data Guide
*this assumes a structure of
root
wordList
word_id_0 //created with .childByAutoId
word: "Water"
You just need to make a single event request on the word child, then you can verify if it .exists() and it will return if it's there or not.
let reference = Database.database().reference().child(<#child#>).child(<#Word#>)
reference.observeSingleEvent(of: .value, with: { (snapshot) in
if snapshot.exists() {
// handle word existing in database, show alert.
} else {
// handle word not existing in database, make a request to set the word in database.
}
})
According to your comments, you wanna iterate through every element on a child, you can do it by doing this:
let reference = Database.database().reference().child("wordList")
reference.observeSingleEvent(of: .value, with: { (snapshot) in
if let words = snapshot.value as? [String : [String : Any]] {
for word in words {
if let currentWord = word.value["word"] as? String {
if currentWord == "YOUR WORD HERE" {
// handle word in database.
return
}
}
}
// handle word NOT in database.
} else {
// handle empty array.
}
})

Firebase load related data iOS

What's the best way to load "related" data in swift?
Common setup, if I have a list of users all stored under uid node and contains a list of follows which stores uids, something like:
"users" : {
"abc123" : {
"email" : "test#test.com",
"follows" : {
"xyz789" : true
}
},
"xyz789" : { ... }
}
What's the most efficient way of loading in the data for all the users one user follows? Is it best to loop through each of the uid's with observeSingleEvent(of: .value)?
This is the solution I've come up with, but feels somewhat cumbersome:
func loadRelated(user: User, completion: #escaping (Bool, [UserObject]) -> ()) {
let ref = Database.database().reference(withPath: "users/" + user.uid + "/follows")
ref.observeSingleEvent(of: .value) { snapshot in
var uids = [String]()
for child in snapshot.children {
let userData = child as! DataSnapshot
uids.append(userData.key)
}
let userRef = Database.database().reference(withPath: "users")
var users = [UserObject]()
var count = 0
uids.forEach { uid in
userRef.child(uid).observeSingleEvent(of: .value) { snapshot in
let user: UserObject(from: snapshot)
users.append(user)
count += 1
if count == uids.count {
completion(true, users)
}
}
}
}
}
I don't really want to go down the denormalization path and store each users data under the top level user.
If you are decided on using Realtime Database, it is best practice to create another root node in your case called user-follows. You can create a follow at the path user-follows/$uid/$fid by setting the value to true, then on your app you would have to observeSingleEvent for each snapshot key ($fid) at user-follows/$uid.
To avoid having to observe each follow separately, instead of setting the value to true, you can just store the data you need about a user in user-follows/$uid. However, a user may change their username for example and so you would need to keep the data inside each user-follows up to date. You can utilise Firebase Cloud Functions to maintain the user-follows when a user changes their information.
Otherwise, I would suggest looking at Firebase Firestore, where some nesting is allowed.
If you know that your node at /users will always contain few users, you could try to get all the users at once with a observeSingleEvent(of:) at path /users. Then filter the users with the ones who are in ../follows.
This may pull more data but it might be faster (not sure) and will need less code to handle.
In fact your initial implementation is quite performant already. Just make sure to handle correctly failing of observeSingleEvent(of:) or the condition count == uids.count will never be fulfilled.
By the way storing each user under ../follows will just duplicate your data and will be hard to maintain updated. So yes avoid it.

Firebase Query not Executing Properly

I am trying to execute a Firebase query and it doesn't seem to be working properly. I am trying to access the name of a School I have in the Firebase Database. The database looks like this:
Schools {
Random School Name {
schoolLocation: Random Location
uid: random UUID
}
}
If I use the following code to get the info of the "Random School Name", I get a snapshot of null:
databaseReference.child("Schools").queryEqualToValue("Random School Name").observeSingleEventOfType(.Value, withBlock: { (snapshot: FIRDataSnapshot) in
print(snapshot)
}) { (error: NSError) in
self.displayError("Search Failed", message: "We couldn't search for that - \(error.code): \(error.localizedDescription)")
}
If I use this line of code, however, the query gives me the name of the school back, as I would expect. I want to be able to track if there is an error using observeSingleEventOfType:
let query = databaseReference.child("Schools").queryEqualToValue("Florida Institute of Technology")
print(query)
Why isn't this working?
I think you're querying by priority here, which will only work if your schools have a priority (highly unlikely).
If that is indeed the problem, solve it by being explicit about the ordering: queryOrderedByKey(). Then also be sure to loop over the children in the result, since a query will return a list, even if there's only one result:
databaseReference
.child("Schools")
.queryOrderedByKey()
.queryEqualToValue("Random School Name")
.observeSingleEventOfType(.Value, withBlock: { snapshot in
for childSnapshot in snapshot.children {
print(snapshot)
}
})
Hey Dan you should try this query for retrieving specific data
self. databaseReference.queryOrderedByChild("Schools").queryEqualToValue("Random School Name").observeSingleEventOfType(.Value, withBlock: { snapshot in
print(snapshot.value)
})
This query returns the random school name for the key schools

containedIn query for Firebase

Coming from Parse, I have heavily relied on the containedIn query to collect the right data. In Parse, I might have had an array of objectIds and queried for all objects with those ids. I am looking to achieve the same on Firebase.
I understand that it is important to flatten data, but I don't see how this helps with the problem. Let's say I have a chat room with a list of users inside it. I collect that data and now have an array of usernames. I would like to now navigate to the users in the database and retrieve all that match one element inside this username array. How can I accomplish something like this?
For example, a set of users in a official Firebase example:
{
"users": {
"alovelace": { ... },
"ghopper": { ... },
"eclarke": { ... }
}
}
I would like to perform a query to download the following users:
["alovelace", "eclarke"]
While a general answer would be helpful, an answer in Swift would be best. Thank you.
An example is that they are the two members of a chat room. Or that
the current user is following them.
So a theoretical users node
users
alovelace
followed_by
bill: true
frank: true
in_chat_room: room_42
location: France
ghopper
followed_by
jay: true
in_chat_room: room_27
location: USA
eclarke
followed_by
frank: true
in_chat_room: room_42
location: Canada
and some chat rooms
chat_rooms
room_27
ghopper: true
room_42
lovelace: true
eclarke: true
To get the detailed users nodes of users in chat room 42 (lovelace, eclarke)
let usersRef = self.myRootRef.childByAppendingPath("users")
usersRef.queryOrderedByChild("in_chat_room").queryEqualToValue("room_42")
.observeEventType(.Value, withBlock: { snapshot in
for child in snapshot.children {
let location = child.value["location"] as! String
print(location) //prints France and Canada
}
})
To get the users Frank is following (lovelace, eclarke):
let usersRef = self.myRootRef.childByAppendingPath("users")
usersRef.queryOrderedByChild("followed_by/frank").queryEqualToValue(true)
.observeEventType(.Value, withBlock: { snapshot in
for child in snapshot.children {
let userName = child.key as String
print(userName)
}
})
Note that using user names as node key's is generally a bad idea - they should be stored by their uid.
Also note we didn't really do anything with the chat_rooms node but to maintain the relationship, having nodes refer to each other can be useful for observing changes etc.
Edit:
In response to a comment, here's the structure for each user to show who they are following instead of who is following the user
users
alovelace
following_user
bill: true
frank: true
in_chat_room: room_42
location: France
with this structure, alovelace is following bill and frank.
To get all of the users following frank:
let usersRef = self.myRootRef.childByAppendingPath("users")
usersRef.queryOrderedByChild("following_user/frank").queryEqualToValue(true)
.observeEventType(.Value, withBlock: { snapshot in
for child in snapshot.children {
let userName = child.key as String
print(userName)
}
})

Resources