I'm trying to get the key of a node based on the child. Here's my structure
Code:
RESTAURANTS
KTNfWKLD0isCsrpys
name: “McDonalds”
loc: “LA”
KTNfWILD0iIkLLekc
name: “KFC”
loc: “LV”
Code:
FIRDatabase.database().reference().child("RESTAURANTS").queryOrderedByChild("name").queryEqualToValue("McDonalds").observeEventType(.Value, withBlock: { (snapshot) in
print(snapshot.key)
}, withCancelBlock: nil)
How can I get the key of "McDonalds" in a query?
You have to use allKeys to get keys from snapshots and than loop it to get key one by one. Try this code
FIRDatabase.database().reference().child("RESTAURANTS").queryOrderedByChild("name").queryEqualToValue("McDonalds").observeEventType(.Value, withBlock: { (snapshot) in
if snapshot.exists() {
for key in (snapshot.value?.allKeys)!{
print(key)
}
}
}
As #Frank van Puffelen said, This way of iterating loses the ordering of the child nodes. If you want to get ordered list than use snapshot.children first
Related
Im currently learning ropes in Firebase for iOS, so bare with my novice skills.
Below is a screenshot of my database:
gZ7dn2LMkiah is the autoId for a user
LDBTRFVS8dtz and LDBTe4oXs are autoIds for childId for this user
How can I read the two child nodes inside the node gZ7dn2LMkiah? Cause from my code below, it can only be possible if I have only one child underneath this node, not two
ref = Database.database().reference()
let userId: String = (Auth.auth().currentUser?.uid)!
databaseHandle = ref?.child("childId").child(userId).observe(.childAdded, with: { (snapshot) in
I tried adding childByAutoId after child(userId) but it didn't return any results.
Any help would be appreciated. Thanks in advance.
Database.database().reference(withPath:
"childId").child(userId).observe(.childAdded)
{ (snapshot:DataSnapshot) in
// This will print all sub node
print(snapshot)
}
First of all your db seems incorrect. In first node nick and relation are inside the autoGeneratedKey and in second node nick and relation are outside the key as both are visible while the node is collapse. So these values should be inside autoGeneratedKey. Please change your db structure in correct way. See below screenshot:
This will be your childs table containing all childs for all parents and you can query for a particular parent to get his childs. See below code snippet:
ref.child("childs").queryOrdered(byChild: "parentId").queryEqual(toValue: "123").observeSingleEvent(of: DataEventType.value) { (snapshot) in
if snapshot.exists() {
print("exists")
for child in snapshot.children {
let data = child as! DataSnapshot
print(data.key)
print(data.value)
}
}
else {
print("doesn't exist")
}
}
Output:
-LDBus9Xas3oTccwPN4r
Optional({
nick = Dave;
parentId = 123;
relation = Son;
})
-LDBus9_Uz_qe69e9CXK
Optional({
nick = Susan;
parentId = 123;
relation = Daughter;
})
Where parentId is let userId: String = (Auth.auth().currentUser?.uid)!.
Note 1: I tried adding childByAutoId after child(userId) but it didn't return any results.
Yes it will not work because childByAutoId generate a new key which will never match with existing db keys so you will get nothing.
Note 2: When to use .childAdded
.childAdded event is to listen new entry for the node reference not to fetch the data. To fetch the data for once we should use .observeSingleEvent event.
Note 3:Cause from my code below, it can only be possible if I have only one child underneath this node, not two
No its not possible. This is just because of second node's nick and relation are outside of the key.
I got score node in Firebase:
And under score node my data structure is as:
{
"jjTO29ziQ2SjrH5PFzY7ZMpSgjq1-Kak6FCyGtdy_kcEPd4K" = {
jjTO29ziQ2SjrH5PFzY7ZMpSgjq1 = "-Kak6FCyGtdy_kcEPd4K";
score = 5;
};
"jjTO29ziQ2SjrH5PFzY7ZMpSgjq1-KbE_pgfUsukOm4uW0bx" = {
jjTO29ziQ2SjrH5PFzY7ZMpSgjq1 = "-KbE_pgfUsukOm4uW0bx";
score = 4;
};
Question:
Can I filter the data by score ?
I tried doing :
FIRDatabase.database().reference().child("scores").queryOrdered(byChild: "score").observeSingleEvent(of: .value, with: { (snapshot) in
debugPrint(snapshot.value ?? "nil")
})
But can't get the result ?
When you execute a query against Firebase, you get a FIRDataSnapshot that contains three pieces of information for each result:
its key
its value
its position relative to other nodes
When you're calling snapshot.value the snapshot is converted into a Dictionary. Unfortunately that dictionary can only hold key-value pairs, so you lose the information on the position of the nodes. So while the data in the snapshot is actually sorted correctly, you're throwing that information away.
The solution is to use the FIRDataSnapshots built-in children property to loop over the child nodes in the correct order. The Firebase documentation on querying has this sample of how to do that:
_commentsRef.observeEventType(.Value, withBlock: { snapshot in
for child in snapshot.children {
...
}
})
The following is my database design in firebase
- Sports
- MainCategory (hypothetical category name)
- SubCategory1
- K_tgbBt6Vx0-kkg7e63
- name:football
- K_tgbBt6Vx0-kkg7a99
- name:golf
- Subcategory2
- K_tgbBt6Vx0-kkgad21
- name:Snooker
- K_tgbBt6Vx0-kkg7e00
- name:Table Tennis
- MainCategory2
- SomeOtherSubCategory1
-K_tgbBt6Vx0-kkg7e00
My Aim: To get all the sports under the sports node and display them category wise(i.e MainCategory and Sub category) in a uicollectionview and allow the user to select sports of his choice.
Eg. display
Main Category1
SubCategory1
Football, Golf
SubCategory2
Table Tennis, Snooker
Main Category2
SomeOtherSubCategory1
Hockey,Tennis
The following approaches only take me one level deeper in the node
rootref.child("sports").observe(.value, with: { (snapshot) in
let mainCategory = snapshot.key
for child in snapshot.children{
print(child)
}})
rootref.child("sports").observe(.childAdded, with: { (snapshot) in
let mainCategory = snapshot.key
let mySnapshot = snapshot.value! as! NSDictionary
}) { (error) in
print(error.localizedDescription)
}
Also if there is any other way i could structure my database, kindly let me know
`rootref.child("sports").observe(.value, with: { (snapshot) in
//This gets your mainCategory names
let mainCategory = snapshot.key
for child in snapshot.children{
print(child)
}})
rootref.child("sports").observe(.childAdded, with: { (snapshot) in
let mainCategory = snapshot.key
let mySnapshot = snapshot.value! as! NSDictionary
}) { (error) in
print(error.localizedDescription)
}
//Now what you need to do is another snapshot to get the sub categories. It would be something like this:
rootref.child("sports").child("Sub-Category").observe(.value, with: { (snaps) in
//Same steps as you did above
})
`
It's cool to know that a man like you with 2.5k reps is asking such a question. I assume you know MVC, of course in the future, you'd want a reusable Service Class to handle Firebase requests and Model Class to handle the data.
Next, you should know the different ways of observing data on Firebase. There is a Single Event and Event of Type. Read the doc: https://firebase.google.com/docs/database/ios/read-and-write
In structuring your database, Firebase has a doc for that too: https://firebase.google.com/docs/database/web/structure-data
Anyway, I made a sample for you, take a look:
http://www.jsoneditoronline.org/?id=5d9e7067883a538746ace7cdd9e81ebb
I made a new structure, which I believe a better structure, of your databse using jsoneditoronline website. Avoid so much nested nodes as much as possible. See my sample, I made necessary new nodes for faster and easier fetching of data. For example, if you're going to view the link above and download the database and upload it to your Firebase Database, you'll see the structure like so:
As you can see, I have here a parent node called subcategories-sportId, which has child nodes of different subcategories and each of that subcategories, we have the ids of the sports.
Another example, if we would like to get all the sports under subcategory2, we won't be using the data inside our sport node. (see below pic) But instead, we will check the data inside the subcategories-sportid node. Get a reference to that node plus add a child node of the specific subcategory string, then fetch all the sportsIds. Enumerate the sportsIDs and lastly fetch the main data of each sports.
But if we would like to get all the subcategories and main categories of the specific sports, we can use the data inside our sport node (see the above pic)
let cat3 = db.child("API Questions").child("Category3").child("Name")
cat3.observe(FIRDataEventType.value, with:
{ (snapshot) in
let eventDataloc = snapshot.value as? [String: AnyObject] ?? [:]
// self.Cat3Qarray.removeAllObjects()
for (_, value) in eventDataloc
{
let studsmodel = FirebaseStructureCustVM.updateQuestionData(Questiondata: value as![String:Any])
self.Cat3Qarray.add(studsmodel)
//print(studsmodel)
}
self.tableview1.reloadData()
//print snapshot here
})
In the below, the "API Questions" is the "root" and the "Category1" is the "child" of that root if we want to get the "Category1" data such as MaxMarks:"20",Q:"General information",Qid:"Question 1" so you can print the snapshot like this === print("the snap of the cat1 = (snapshot)") === you will get all the data which is inside of the Category1
let cat2 = db.child("API Questions").child("Category1")
cat2.observe(FIRDataEventType.value, with:
{ (snapshot) in
let eventDataloc = snapshot.value as? [String: AnyObject] ?? [:]
for (_, value) in eventDataloc
{
let studsmodel = FirebaseStructureCustVM.updateQuestionData(Questiondata: value as![String:Any])
}
print((snapshot))
})
When I create objects in Firebase, I use childByAutoId. How can I update these specific objects later? I'm having trouble obtaining the value of the key Firebase automatically updates. Snapshot.key just returns "users". Here's my JSON structure:
{
"users" : {
"-KQaU9lVcUYzIo52LgmN" : {
"device" : "e456f740-023e-440a"
"name: "Test"
}
},
How can I get the -KQaU9lVcUYzIo52LgmN key? I want to update the device child. Here's what I have so far. It currently creates a completely separate snapshot with a single child.
self.rootRef.child("users").queryOrdered(byChild: "name").queryEqual(toValue: self.currentUser).observeSingleEvent(of: .value, with: { (snapshot) in
let key = self.rootRef.child("users").childByAutoId().key
let childValues = ["device": device]
self.rootRef.child("users").child(key).updateChildValues(childValues)
Edit: device is a string set further up in the code. Not defined in this scope (to make it easier to read for this question).
When you get Snapshot.key, it returns "users" because that is the overall key for your snapshot. Everything inside of "users" in your snapshot is considered the value.
You need to iterate over the child layers to dig down to "device".
Try this:
rootRef.child("users").observeSingleEventOfType(.Value, withBlock: { (snapshot) in
if let result = snapshot.children.allObjects as? [FIRDataSnapshot] {
for child in result {
var userKey = child.key as! String
if(userKey == userKeyYouWantToUpdateDeviceFor){
rootRef.child("users").child(userKey).child("device").setValue(device)
}
}
}
})
This code will do the following:
Gets snapshot of your reference (the key for that would be
'users').
Gets all the children (your user keys) and assigns them as another
snapshot to 'result'.
Checks each key one at a time until it finds the key you want (for
example, if you look for user with the key "-KQaU9lVcUYzIo52LgmN",
it will find it on the first iteration in your example code you
posted).
Once it finds that key, it sets the value for the device inside that
key with the line
rootRef.child("users").child(userKey).child("device").setValue(device).
Of course, you will need to store all your user keys when you make them. You can maybe use SharedPreferences on the device for this, but if it gets cleared for any reason then that data will just be sitting there. You could also store it on internal storage for your app, but SharedPreferences is what I would use.
Hope this helps!
snapshot has a property key which is
The key of the location that generated this FIRDataSnapshot.
And as you can see you are getting one (snapshot) by calling observeSingleEvent(of: .value, with: { (snapshot)...
so instead of let key = self.rootRef.child("users").childByAutoId().key
try to call let key = snapshot.key
childByAutoId().key always generates new unique key based on timestamp, that's why you are creating new child, not updating the one you want
Hope that works
I adapted Ryan's answer to my own issue (kinda similar) and figured out a way to update your device ID directly without needed to know/store the AutoID key generated by Firebase :
reference = Database.database().reference().child("users")
reference.observeSingleEvent(of: .value, with: { (snapshot) in
if let result = snapshot.children.allObjects as? [DataSnapshot] {
for child in result {
if child.childSnapshot(forPath: "device").value as? String == self.yourDeviceIDVariable {
print("### Device exists in Firebase at key = \(child.key)")
let childKey = child.key
self.reference.child(childKey).child("device").setValue(yourNewDeviceID)
}
}
}
})
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