How to represent a two ways relationship in a Fire

2019-03-13 15:59发布

问题:

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?

回答1:

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.