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:
- Company
- West Region
- Division 1
- Department 1
- Department 2
- Division 2
- Department 3
- Department 4
- Division 1
- South Region
- Division 3
- Department 5
- Department 6
- Division 3
- West Region
- Company 2 ...etc.
At the lowest (department) level, I have orders whose amounts I have to aggregate.
First Attempt
Data (Denormalized)
{
"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
}
}
}
}
Rules
{
"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())"
}
}
}
Conclusion
Pros
- Querying is flexible, e.g:
ordersRef.orderByChild('division').equalTo('Division 1')
. - Querying is fast. This works in under 2 seconds for 200k records.
Cons
- I don't believe I can restrict access with kind of a structure. I get
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".
Second Attempt
Data (More Normalized)
{
"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
}
}
}
}
Rules
{
"Orders": {
".read":false,
".write":false,
"$order_id": {
".read": "root.child('Users').child(auth.uid).child('departments').hasChild(data.child('department').val())"
}
}
}
Conclusion
Pros
- I can restrict access using security rules now by reading one department at a time.
Cons
- I don't think I can do a deep AND variate index on "Orders/$order_id/" +company, region, or division, which means I have to make a separate call for each department's orders to aggregate at some higher level.