I am trying to retrieve data from two tables in a database. I understand that if I want to read from a child I do this:
mDatabase = FirebaseDatabase.getInstance().getReference().child("Users");
So what do I do for two children?
My data looks like this:
I'll assume 2 possibilities for your question.
First: you want to retrieve a child of a child
appName{
"users" : {
"Bob" : {
"age" : 20
}
}
In this case to access a specific user you should do:
mDatabase = FirebaseDatabase.getInstance().getReference().child("Users").child("Bob");
But if you mean that you have different child like: users and questions
appName{
"users" : {
"Bob" : {
"age" : 20
}
}
"questions" : {
"who am I?"
}
}
Then, you should make a reference to each one of them:
usersRef = FirebaseDatabase.getInstance().getReference().child("Users");
questionsRef = FirebaseDatabase.getInstance().getReference().child("questions");
Related
I have an ORDER structure in ABAP that has another ITEMS structure within it which will contain multiple items per order.
I'm populating this structure through a SAP Gateway service, which works for an ORDER + a single ITEM.
{
"d": {
"Venueid": "dsfgg",
"Items" : {
"__metadata" : {
"type" : "ZGW_XXXX_SRV.Items"},
"Venueid" : "dsd",
"Type" : ""
}
}
}
However, what would be the syntax to provide an array of more than one ITEM?
The issue was because multiples can only be handled with the DEEP_INSERT method, not the normal CREATE.
I have a value which needs to be compared with an array of values. Basically a user needs to check if it has the same item as another user. But I am struggling to solve this as how do I get the item of an array of users? Normally when you observe a value you do something like this:
Database.database().reference(withPath: "users/\(userID)/itemList").observeSingleEvent...
However, when it comes to an array of users itemList how can this be achieved if there are multiple ID's? I'd like compare a user item or items with other users item and check if they match in order to sort an array of users that have a match.
If there is already an example for this please help direct me there.
Update
This is how my data structure looks:
{
"users": {
"EWJGFYmVTzOiHgnq42ebhrg2fj": {
"firstName": "Friederike",
"itemList": [
2: true,
3: true,
0: true
]
},
"C076OYmVTzOiHgnq4wPQtY2XpED2": {
"firstName": "Ian",
"itemList": [
0: true,
1: true,
3: true
]
},
"Juoiuf0N6qNmkm32jrtu6X6UK62": {
"itemList": [
0: true
],
"firstName": "Jack"
}
}
}
Update 2.0
With the answer below I am able to query through the items table but still unable to query the keys that match as there can be multiple arrays and therefore I cannot use it to filter or anything.
Ok so with your current data structure you'd have to query all nodes under users and then compare the arrays, which is very inefficient. There isn't a straight forward or easy way to do that without modifying your structure. So I suggest you modify your data structure so that each item has a list of all users that have it. Something like this:
{
"items": {
"0": {
"EWJGFYmVTzOiHgnq42ebhrg2fj": "Friederike",
"C076OYmVTzOiHgnq4wPQtY2XpED2": "Ian",
"Juoiuf0N6qNmkm32jrtu6X6UK62": "Jack"
},
"1": {
"C076OYmVTzOiHgnq4wPQtY2XpED2": "Ian"
},
"2": {
"EWJGFYmVTzOiHgnq42ebhrg2fj": "Friederike"
}
//....
}
}
Depending on what you want to display you might want to store more information than just the users UID and username. You can query all the users that have the same items as you using a query like this:
let ref = Database.database().reference()
// assuming you already have the current users items stored in an array
for item in items {
ref.child("items").child(String(item)).observeSingleEvent(of: .value, with: { snap in
for child in snap.children {
let child = child as? DataSnapshot
if let key = child?.key, let name = child?.value as? String {
// do something with this data
}
}
})
}
Firebase database is noSQL, so data is meant to be denormalized or duplicated so that queries can be optimized. Firebase actually recommends that you avoid nesting data. Take a look at this question for more information.
Hope that helps
Code related to question asked in comments
Assuming you are storing the UID's or names of users with the same items in a string array you can prevent duplicates using .contains()
var namesWithMatchingItems = [String]()
if !namesWithMatchingItems.contains(nameYouJustFetched) {
namesWithMatchingItems.append(nameYouJustFetched)
}
I have an array of User ID Strings and I want to query data in Firebase Realtime Database to see if each User ID has a specific gender.
var nearbyUserArray = [String]()
Below is an example of the data I have in Firebase. The second level deep is the User ID (0UFhIgGAwIY1btnBeNaNkJruYKJ3). What I am trying to do is loop through my User ID array and for each User ID check if that specific user is Male. If the user is Female, remove that User ID from the array.
"profiles" : {
"0UFhIgGAwIY1btnBeNaNkJruYKJ3" : {
"dateOfBirth" : "May 11, 1987",
"gender" : "Female",
"higherAgeRange" : 36,
"interestedIn" : "Male",
"lowerAgeRange" : 29,
"name" : "Sally"
},
"6vNjngZqoTbd4oGudip3NX78pu32" : {
"dateOfBirth" : "December 23, 1990",
"gender" : "Male",
"higherAgeRange" : 39,
"interestedIn" : "Female",
"lowerAgeRange" : 25,
"name" : "Bob"
}
How would you recommend I accomplish this? I want to minimize the amount of data I have to download and manipulate on the client side. Below is what I am attempting to do but I cannot get it to work. Any ideas why this always returns Null?
func getUsersWithinGenderCriteria() {
for user in nearbyUserArray {
DatabaseService.shared.profilesRef.child(user).queryOrdered(byChild: "gender").queryEqual(toValue: "Female").observeSingleEvent(of: .value) { [weak self] (snapshot) in
print("snap: \(snapshot)")
// The idea is if snapshot is Female, remove the user ID from the array. If the snapshot is Null, keep it in the array. However, this is not working as all snapshots come back as null.
}
}
}
Also, here are the security rules I set up.
"profiles": {
"$userId": {
".indexOn": ["gender"]
}
}
When you perform a query, Firebase compares a property of each child node under the location you query to the value you pass in.
I assume that profilesRef points to /profiles in your database. In that case profilesRef.child(user) in your code points to the profile for a specific user already. When you then run a query, you are querying each of the properties of that specific user.
What you like want to do is to get all users with gender equal to Female. In that case you should query the user list:
DatabaseService.shared.profilesRef.queryOrdered(byChild: "gender").queryEqual(toValue: "Female")...
Note that you also should define the index on the level where the query is run, so on /users:
"profiles": {
".indexOn": ["gender"]
}
Another alternative (the might scale better) is to not use a query altogether, but instead load the gender property of each individual user in your array. In (approximate) code:
for user in nearbyUserArray {
DatabaseService.shared.profilesRef.child(user).child( "gender").observeSingleEvent(of: .value) { [weak self] (snapshot) in
if snapshot.value == "Female" {
print("snap: \(snapshot)")
}
}
}
I am working on a group chat application, and I'm running into trouble trying to make a specific query to Firebase DB.
I need to get all of the profile pictures of users in a room. But only the rooms that the current user is a part of.
private func observeRooms() {
let databaseRef = FIRDatabase.database().reference()
let groupRef = databaseRef.child("groups")
guard let uid = FIRAuth.auth()?.currentUser?.uid else {return}
let queryRef = groupRef.queryOrdered(byChild: "participants/\(uid)").queryEqual(toValue: nil)
queryRef.observe(.childAdded, with: { snapshot in
let roomDict = snapshot.value as! [String: AnyObject]
print("roomDict: \(roomDict)")
let id = snapshot.key
let avatar = roomDict["profilePicture"] as! String
})
}
Obviously setting queryEqual(toValue: nil) returns all the rooms that the current user is NOT a part of - the opposite of what I need. I know the inverse will be to check queryEqual(toValue: true), but the : true is not showing up in the database.
This is how I add a node to "groups" in Firebase:
// Start group node for new room
let groupRef: FIRDatabaseReference = FIRDatabase.database().reference().child("groups")
let roomID = newRoomRef.key
let groupRoomRef = groupRef.child(roomID)
let groupDict = ["roomName" : roomName, "participants": [uid : true]] as [String : Any]
groupRoomRef.setValue(groupDict)
And this is how the data structure ends up:
"groups" : {
"-KkGzZ7frbnXmImt0JpV" : {
"participants" : {
"tzfHgGKWLEPzPU9GvkO4XE1QKy53" : {
"gender" : "male",
"handle" : "Testing",
"name" : "Test User",
"profilePicture" : "https://graph.facebook.com/*removed*/picture?type=large&return_ssl_resources=1",
"status" : "F8B016"
}
},
"roomName" : "Test Room"
},
How can I properly the profilePicture values for all users in a room, only for the groups that the current user's UID is a participant of?
You're on the right track. When using queryEqual your query needs to match the value actually present in the database. Unfortunately that gets complicated when the values you're querying against are complex objects. In fact I'm not sure the function will work even if you did match the entire object value. The issue here is that you're trying to look up objects by the presence of a child key, instead of actually sorting by or matching their values. That doesn't quite fit the use case of any of the orderBy methods.
There are a few ways to work around this, ordered by effort.
First, you could take advantage of the sort order to get only those who have some object with the name of the UID. Since Firebase sorts nulls first, then primitives, and finally objects, you should be able to order by child and start at true (or any primitive) to get all object instances with that key (assuming, of course, you're not storing UID: true anywhere).
groupRef.queryOrdered(byChild: "participants/\(uid)").queryStartingAtValue(true)
Second, duplicate the UID as a key inside the object with the value true so that you can sort on it directly. Notice the extra step in the query.
"groups" : {
"-KkGzZ7frbnXmImt0JpV" : {
"participants" : {
"tzfHgGKWLEPzPU9GvkO4XE1QKy53" : {
"tzfHgGKWLEPzPU9GvkO4XE1QKy53" : true,
...
groupRef.queryOrdered(byChild: "participants/\(uid)/\(uid)").queryEqual(toValue: true)
Finally, and this is what I recommend, reconsider your data structure. You're nesting a lot of data inside this collection of collections that is going to make your queries and updates unwieldy. Firebase tends to work better when you denormalize your data, preferring flat/shallow structures with some duplication of simple data. In your case, instead of storing the user's profile data inside of their group membership, you might have a collection of groups and another collection of users, with each group containing only a list of UIDs to its members and vice versa. The example in the documentation is a very similar case to yours involving membership in chat conversations. At that point your UIDs (and group IDs, if you're querying on users instead) will have a value of true so you can again use a simple value query.
"groups" : {
"-KkGzZ7frbnXmImt0JpV" : {
"participants" : {
"tzfHgGKWLEPzPU9GvkO4XE1QKy53" : true
...
"users" : {
"tzfHgGKWLEPzPU9GvkO4XE1QKy53" : {
"groups" : {
"-KkGzZ7frbnXmImt0JpV" : true
...
groupRef.queryOrdered(byChild: "participants/\(uid)").queryEqual(toValue: true)
I have users structure lke this:
{
"users": {
"uniqueID1": {
"name": "Anon",
"friends": {
"uniqueID2": true,
"uniqueID3": true
}
}
"uniqueID2": { },
"uniqueID3": { },
}
}
I want to show a user's friends' names. I have to access $user/friends/ to get list of unique IDs, and iterate the list to get friend's information. But iterating the unique ID is making multiple queries, and I have to always check if all of my queries are finished. According to the doc, it seems multiple queries will not impact the performance too much, but if I want to update my view only when all of the queries are finished, I have to check how many queries are finished.
Is there no way of 'execute a completion block when all queries are finished'?
Pseudocode
var totalNumOfFriends = 0
var tempArray = NewArray()
ref(/users/uniqueID1/friends).observeEventType{ snapshot
var uIDList = snapshot.children's keys
totalNumOfFriends = uIDList .count
for uID in uIDList {
var nameRef = ref(/users/uID/name) i.e. /users/uniqueID3/name
nameRef.observeSingleEventOfType { snapshot
var username = snapshot.value
tempArray.append(username)
if tempArray.count == totalNumOfFriends {
// If counts are the same, tempArray has all of my friends' names
// Now update view using tempArray
}
}
}
}
Pseudocode explanation:
Get list of unique IDs from /users/uniqueID1/friends
'Save' number of unique IDs. (Explained in step 4)
For each unique IDs from the list, get user's name by using ref like this /users/uniquedID2/name
For each name retrieved, add it to temporary array. Once the count of the temporary array equals to the count from step 2, update my view as I have retrieved all the names.
Firebase has no built-in way to signal when a number of queries has finished. But you can easily implement this in your own code. Your approach with a counter that checks how many items have already been loaded is the most common approach for that.