Reputation: 45
How can I give users access to data for anything below a certain node in a hierarchical structure AND make it easy to query? Can this be done in Firebase, or do I have to abandon my beloved Firebase and go back to...grumble grumble...RDBMS?
I've tried two different ways. One makes it easy to query but hard to restrict access. The other makes it easier to restrict access but means I have to do nested loops to aggregate all my data.
Specifically, I have a typical business organization:
At the lowest (department) level, I have orders whose amounts I have to aggregate.
{
"Orders": {
"UniqueID1": {
"company": "Company",
"region": "West Region",
"division": "Division 1",
"department": "Department 1",
"amount": 19.8
},
"UniqueID2": {
"company": "Company",
"region": "West Region",
"division": "Division 1",
"department": "Department 1",
"amount": 20.1
},
...and so on.
},
"Users": {
"Bob's UID": {
"departments": {
"Department 1": true, // Note that these two departments combined are Division 1
"Department 2": true
}
}
}
}
{
"Orders": {
".indexOn": ["company", "region", "division", "department"],
".read":false,
".write":false,
"$order_id": {
".read": "root.child('Users').child(auth.uid).child('departments').hasChild(data.child('department').val())"
}
}
}
ordersRef.orderByChild('division').equalTo('Division 1')
.permission_denied
based on the rule I have above. I think it's because I've run into the issue of "rules are not filters, tsk tsk tsk".{
"Orders": {
"Department 1": {
"UniqueID1": {
"company": "Company",
"region": "West Region",
"division": "Division 1",
"amount": 19.8
},
"UniqueID2": {
"company": "Company",
"region": "West Region",
"division": "Division 1",
"amount": 20.1
},
},
"Department 2": {...
...and so on.
},
"Users": {
"Bob's UID": {
"departments": {
"Department 1": true, // Note that these two departments combined are Division 1
"Department 2": true
}
}
}
}
{
"Orders": {
".read":false,
".write":false,
"$order_id": {
".read": "root.child('Users').child(auth.uid).child('departments').hasChild(data.child('department').val())"
}
}
}
Upvotes: 4
Views: 596
Reputation: 598728
Your first data structure looks reasonable. You can access specific orders you have access to, and you already can find the list of the divisions for a specific user.
To also allow accessing the order for (for example) a division, you'll need to add a secondary index that maps a division to a list of order IDs:
{
"Orders": {
"UniqueID1": {
"company": "Company",
"region": "West Region",
"division": "Division 1",
"department": "Department 1",
"amount": 19.8
},
"UniqueID2": {
"company": "Company",
"region": "West Region",
"division": "Division 1",
"department": "Department 1",
"amount": 20.1
},
...and so on.
},
"Users": {
"Bob's UID": {
"departments": {
"Department 1": true, // Note that these two departments combined are Division 1
"Department 2": true
}
}
},
"OrdersByDivision": {
"Division 1": {
"UniqueID1": true,
"UniqueID2": true
}
}
}
Now you can find the list of orders for a division with a direction lookup under OrdersByDivision
and then loop to load the orders:
ref.child('OrdersByDivision/Division 1').on('child_added', function(snapshot) {
ref.child('Orders').child(snapshot.key).once('value', function(order) {
console.log(order.val());
});
});
Most developers that have done web development without Firebase are afraid this pattern will be slow. But given that Firebase retrieves the items over an already established connection, this is actually quite fast. See Speed up fetching posts for my social network app by using query instead of observing a single event repeatedly
Upvotes: 1