Warning:
- this is an exercise to understand better JSON database design in Firebase
- it is not necessarily realistic
I have got a two ways relationship between users and door keys. I would like to understand:
- how to represent this relationship visually (I can imagine it only as two separate trees)
- how this would work on Firebase, would both users and door-keys be child of a parent node "myparentnodename"?
- If I model the database in this way it feels highly inefficient because every time I would query the child node "users" I would get all the users back. Or am I wrong? Is it possible to only get back the data matching to a specific user? E.g. get user where "user = user1"? Can we do nested queries? e.g. combine the previous condition with some condition on the door keys so the JSON object returned is only relevant to the door-keys contained in the "user1" node?
This is a very long answer as your question was actually about 5 different questions.
root node is: myparentnodename
Your users
users
uid_0
name: "William"
door_keys:
key_0: true
key_3: true
uid_2
name: "Leonard"
door_keys:
key_3: true
key_5: true
and your keys
keys
key_0
uid_0: true
key_3
uid_0: true
uid_2: true
key_5
uid_5: true
With this structure, all of the elements 'point' at each other.
If you query uid_0 you can see that they use keys 0 and 3
If you query key_3 you can see they belong to users 0 and 2
Your question was
every time I would query the child node "users" I would get all the
users back
That's slightly incomplete. When a query is done, you usually query for something specific. With Firebase however, there are two ways to retrieve data: observing a node and a query.
If you want back all users in the users node you would observe that node by .Value (or .ChildAdded for 1 at a time).
ref = myParentNodeName
let usersRef = myParentNodeName.childByAppendingPath("users")
usersRef.observeEventType(.Value, withBlock: { snapshot in
//.Value can return multiple nodes within the snapshot so iterate over them
for child in snapshot.children {
let name = child.value.objectForKey("name") as! String
print(name) //prints each users name
}
})
note that the above attaches an observer to the users node so any future changes within that node will notify the app and re-send the entire node to the app
If you want just one user's info, and don't want to continue watching for changes
ref = myParentNodeName
let usersRef = myParentNodeName.childByAppendingPath("users")
let thisUserRef = usersRef.childByAppendingPath("uid_2")
thisUserRef.observeSingleEventOfType(.Value, withBlock: { snapshot in
let name = child.value.objectForKey("name") as! String
print(name) //prints each users name
})
Finally, to query for all keys that belong to uid_0 (which is a little redundant in this example since we already know which keys they have from their node). If the keys ref also contained other info like the door name, the building the door was in, or the door location, it would be more appropriate and would require a different structure, so assume that's the case:
ref = myParentNodeName
let keysRef = myParentNodeName.childByAppendingPath("keys")
keysRef.queryOrderedByChild("uid_0").queryEqualToValue(true)
.observeSingleEventOfType(.Value, withBlock: { snapshot in
let doorLocation = child.value.objectForKey("door_location") as! String
print(doorLocation) //prints each users name
})
note this code is Swift since the platform was not specified in the question.
The other question:
Can we do nested queries? e.g. combine the previous condition with
some condition on the door keys so the JSON object returned is only
relevant to the door-keys contained in the "user1" node?
I think you mean can you query for uid_2, see which keys they have and then load in the info from those specific keys.
Yes! But... (there's always a but)
Firebase is asynchronous so you have to take that into account when nesting queries i.e. you need to ensure all of the data is returned before getting more data. So for example, if you wanted uid_2 key data, you could observeSingleEventOfType on node uid_2. You would then have their keys and could then observeSingleEventOfType on each key.
Technically this will work but with asynchronous data flying around, you could end up with code stomping on other code and processing data before it's actually been returned.
The better option (per my example) is to just avoid that entirely and query the keys node for uid_2's keys.
As a side note, observing a node has a lot less overhead than a query so if you want to load a single node and you know the path, use observe.