Structuring the Cloud Firestore Database

381 views
Skip to first unread message

herantd

unread,
Apr 12, 2019, 4:21:25 PM4/12/19
to Firebase Google Group
Hi,

I am currently working on an PWA that will serve as a company portal. The idea is that the user who is employed by company (A) will be part of a team, let's say team: "red". The user who logs in will only have access to the people who are on the same team as this user. For example, company (A) will consist of several teams, and the portal will consist of several companies.
I wonder what is the best way to structure the database so that at a later time if the administrator wants to move a user over to another team then it is as easy as just a keystroke.
I have created a database that starts with "users", then contains a list of documents that act as a user ID. Inside the collection, it says which company the user belongs to and which team and the ID are equal to the user's login (Authentication) id. I am quite new with databases and firebase, so it would have been nice to know what is the best way to construct such a system.




database_option_1.PNG

Kato Richardson

unread,
Apr 12, 2019, 5:52:39 PM4/12/19
to Firebase Google Group
First of all, you should create the users keyed by the user id, rather than random document ids. That will help later when you're trying to write security rules and read user accounts.

There are two ways to handle the memberships. If the relationship is always 1:1 and that will never change, you could just namespace each group and move users between the groups. So a structure like follows:

/teams/$team/users/$user
/teams/$team/widgets/$widget

And then you'd simply write security rules to restrict access at the $team level based on whether exists(/databases/$(database)/teams/$(team)/users/$(request.auth.uid));

But the more common structure here is probably to do a bit of duplication, as this is a bit more flexible and allows many-to-many relationships:

/users/$user/teams (an array of team ids)
/teams/$team/members (an array of user ids)

And now your teams path is secured with something like  request.auth.uid in get(/databases/$(database)/teams/$(team)/members).data;

And if you have a user id, you can reverse look up access in the same way:  $(teamId) in get(/databases/$(database)/users/$(request.auth.uid)/teams).data;

I hope that helps!

☼, Kato

--
You received this message because you are subscribed to the Google Groups "Firebase Google Group" group.
To unsubscribe from this group and stop receiving emails from it, send an email to firebase-tal...@googlegroups.com.
To post to this group, send email to fireba...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/firebase-talk/76d32dad-6cd0-449c-854b-c161ab4718ed%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.


--

Kato Richardson | Developer Programs Eng | kato...@google.com | 775-235-8398

jo...@nowims.com

unread,
Apr 12, 2019, 6:00:09 PM4/12/19
to Firebase Google Group
Kato,

What about doing something like

/users/$user
/teams/$team
/userTeams/$user
/teamUsers/$team

or is the array better now that we have the ability to query on arrays?  Also, what is your recommendation if there is a role for that member of the team?


On Friday, April 12, 2019 at 4:52:39 PM UTC-5, Kato Richardson wrote:
First of all, you should create the users keyed by the user id, rather than random document ids. That will help later when you're trying to write security rules and read user accounts.

There are two ways to handle the memberships. If the relationship is always 1:1 and that will never change, you could just namespace each group and move users between the groups. So a structure like follows:

/teams/$team/users/$user
/teams/$team/widgets/$widget

And then you'd simply write security rules to restrict access at the $team level based on whether exists(/databases/$(database)/teams/$(team)/users/$(request.auth.uid));

But the more common structure here is probably to do a bit of duplication, as this is a bit more flexible and allows many-to-many relationships:

/users/$user/teams (an array of team ids)
/teams/$team/members (an array of user ids)

And now your teams path is secured with something like  request.auth.uid in get(/databases/$(database)/teams/$(team)/members).data;

And if you have a user id, you can reverse look up access in the same way:  $(teamId) in get(/databases/$(database)/users/$(request.auth.uid)/teams).data;

I hope that helps!

☼, Kato

On Fri, Apr 12, 2019 at 1:21 PM herantd <hera...@gmail.com> wrote:
Hi,

I am currently working on an PWA that will serve as a company portal. The idea is that the user who is employed by company (A) will be part of a team, let's say team: "red". The user who logs in will only have access to the people who are on the same team as this user. For example, company (A) will consist of several teams, and the portal will consist of several companies.
I wonder what is the best way to structure the database so that at a later time if the administrator wants to move a user over to another team then it is as easy as just a keystroke.
I have created a database that starts with "users", then contains a list of documents that act as a user ID. Inside the collection, it says which company the user belongs to and which team and the ID are equal to the user's login (Authentication) id. I am quite new with databases and firebase, so it would have been nice to know what is the best way to construct such a system.




database_option_1.PNG

--
You received this message because you are subscribed to the Google Groups "Firebase Google Group" group.
To unsubscribe from this group and stop receiving emails from it, send an email to fireba...@googlegroups.com.

To post to this group, send email to fireba...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/firebase-talk/76d32dad-6cd0-449c-854b-c161ab4718ed%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Kato Richardson

unread,
Apr 12, 2019, 6:33:29 PM4/12/19
to Firebase Google Group
I've been working on some complex use cases related to this in the last couple of months. For the example given here, either would do. But arrays seem to be working better overall in real scenarios.

They really start to shine when you have two lists and need to find an intersection. For example, assume that each team had a list of members, and each document we want to access has a list of teams who can view it. Consider how complex that might be working with collections--probably not possible. But having the lists in arrays allows me to do something like  listTeamMembers(teamId).hasAny(listTeamsWithDocAccess(docId));

I've also been working on some even more complex groupings involving inheritance and other complications. Things get complex very quickly, and Functions triggers are needed to map the relationships down to something security rules can understand. The intersectionality of arrays seems to reduce the work involved considerably.

☼, Kato

To unsubscribe from this group and stop receiving emails from it, send an email to firebase-tal...@googlegroups.com.

To post to this group, send email to fireba...@googlegroups.com.

For more options, visit https://groups.google.com/d/optout.

Kato Richardson

unread,
Apr 12, 2019, 6:37:11 PM4/12/19
to Firebase Google Group
Oops, forgot one critical detail. Keep in mind that arrays need to fit in a document (1 MiB max) so if you're working with very large teams you'd need something more sophisticated like work with collections.

☼, Kato

herantd

unread,
Apr 13, 2019, 10:52:10 AM4/13/19
to Firebase Google Group

companies.PNG

teams.PNG

users.PNG

auth_user.PNG

herantd

unread,
Apr 13, 2019, 10:52:10 AM4/13/19
to Firebase Google Group
Could you please provide a screenshot of how it should look like? Been trying to figure out how to set up the database by your description but i'm getting so confused by these id's and how to bind them between users and teams.


fredag 12. april 2019 23.52.39 UTC+2 skrev Kato Richardson følgende:
First of all, you should create the users keyed by the user id, rather than random document ids. That will help later when you're trying to write security rules and read user accounts.

There are two ways to handle the memberships. If the relationship is always 1:1 and that will never change, you could just namespace each group and move users between the groups. So a structure like follows:

/teams/$team/users/$user
/teams/$team/widgets/$widget

And then you'd simply write security rules to restrict access at the $team level based on whether exists(/databases/$(database)/teams/$(team)/users/$(request.auth.uid));

But the more common structure here is probably to do a bit of duplication, as this is a bit more flexible and allows many-to-many relationships:

/users/$user/teams (an array of team ids)
/teams/$team/members (an array of user ids)

And now your teams path is secured with something like  request.auth.uid in get(/databases/$(database)/teams/$(team)/members).data;

And if you have a user id, you can reverse look up access in the same way:  $(teamId) in get(/databases/$(database)/users/$(request.auth.uid)/teams).data;

I hope that helps!

☼, Kato

On Fri, Apr 12, 2019 at 1:21 PM herantd <hera...@gmail.com> wrote:
Hi,

I am currently working on an PWA that will serve as a company portal. The idea is that the user who is employed by company (A) will be part of a team, let's say team: "red". The user who logs in will only have access to the people who are on the same team as this user. For example, company (A) will consist of several teams, and the portal will consist of several companies.
I wonder what is the best way to structure the database so that at a later time if the administrator wants to move a user over to another team then it is as easy as just a keystroke.
I have created a database that starts with "users", then contains a list of documents that act as a user ID. Inside the collection, it says which company the user belongs to and which team and the ID are equal to the user's login (Authentication) id. I am quite new with databases and firebase, so it would have been nice to know what is the best way to construct such a system.




database_option_1.PNG

--
You received this message because you are subscribed to the Google Groups "Firebase Google Group" group.
To unsubscribe from this group and stop receiving emails from it, send an email to fireba...@googlegroups.com.

To post to this group, send email to fireba...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/firebase-talk/76d32dad-6cd0-449c-854b-c161ab4718ed%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

herantd

unread,
Apr 13, 2019, 10:52:10 AM4/13/19
to Firebase Google Group
I'm sorry but i'm sure what you mean by $user by putting "$"sign in front of the name?


fredag 12. april 2019 23.52.39 UTC+2 skrev Kato Richardson følgende:
First of all, you should create the users keyed by the user id, rather than random document ids. That will help later when you're trying to write security rules and read user accounts.

There are two ways to handle the memberships. If the relationship is always 1:1 and that will never change, you could just namespace each group and move users between the groups. So a structure like follows:

/teams/$team/users/$user
/teams/$team/widgets/$widget

And then you'd simply write security rules to restrict access at the $team level based on whether exists(/databases/$(database)/teams/$(team)/users/$(request.auth.uid));

But the more common structure here is probably to do a bit of duplication, as this is a bit more flexible and allows many-to-many relationships:

/users/$user/teams (an array of team ids)
/teams/$team/members (an array of user ids)

And now your teams path is secured with something like  request.auth.uid in get(/databases/$(database)/teams/$(team)/members).data;

And if you have a user id, you can reverse look up access in the same way:  $(teamId) in get(/databases/$(database)/users/$(request.auth.uid)/teams).data;

I hope that helps!

☼, Kato

On Fri, Apr 12, 2019 at 1:21 PM herantd <hera...@gmail.com> wrote:
Hi,

I am currently working on an PWA that will serve as a company portal. The idea is that the user who is employed by company (A) will be part of a team, let's say team: "red". The user who logs in will only have access to the people who are on the same team as this user. For example, company (A) will consist of several teams, and the portal will consist of several companies.
I wonder what is the best way to structure the database so that at a later time if the administrator wants to move a user over to another team then it is as easy as just a keystroke.
I have created a database that starts with "users", then contains a list of documents that act as a user ID. Inside the collection, it says which company the user belongs to and which team and the ID are equal to the user's login (Authentication) id. I am quite new with databases and firebase, so it would have been nice to know what is the best way to construct such a system.




database_option_1.PNG

--
You received this message because you are subscribed to the Google Groups "Firebase Google Group" group.
To unsubscribe from this group and stop receiving emails from it, send an email to fireba...@googlegroups.com.

To post to this group, send email to fireba...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/firebase-talk/76d32dad-6cd0-449c-854b-c161ab4718ed%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

jo...@nowims.com

unread,
Apr 13, 2019, 11:40:39 AM4/13/19
to Firebase Google Group
The method Kato was described above is using an array to store users and teams.  See screenshots below.  The $user is the id of the user and $team = teamId.

Screen Shot 2019-04-13 at 10.35.02 AM.png

Screen Shot 2019-04-13 at 10.35.29 AM.png

herantd

unread,
Apr 14, 2019, 10:47:28 AM4/14/19
to Firebase Google Group
Thanks alot! So in case of several companies:

companies/$company/  -- $user: true
                   |                 |-- $team: true
                   |
                  /$company/  --$user: true
                                     |-- $team: true

right?

Also the userid($user) you provided, is that the id which automatically genereted when authentication user is created?

Inkedforum_user_showcase_LI.jpg

jo...@nowims.com

unread,
Apr 29, 2019, 4:54:57 PM4/29/19
to Firebase Google Group
We need a solution that enables multiple users to belong to multiple organizations with a role per organization?

We are using the following models below

users <-- collection
  name: `John Doe`,
  email: `john@doe.com`

organizations <-- collection
  name: `Fake Co`
  normalizedName: `fake-co`,

  name: `Fake Co 2`,
  normalizedNam: `fake-co-2

user_organizations <-- collection
  user_id1: {
    organization_id1: {
      role: `Owner`,
      name: `Fake Co`,
      organizationRef: firestore.doc.ref
    },
    organization_id2: {
      role: `Guest`,
      name: `Fake Co 2`,
      organizationRef: firestore.doc.ref
    }
  },
  user_id2: {
    organization_id : {
      role: `Member`,
      name: `Fake Co 2`,
      organizationRef: firestore.doc.ref
    }
  }

organization_users <-- collection
  organization_id1: {
    user_id1: {
      role: `Owner`,
      name: `John Doe`,
      userRef: firestore.doc.ref
    },
    user_id2: {
      role: `Guest`,
      name: `John Doe 2`,
      userRef: firestore.doc.ref
    }
  },
  organization_id2: {
    user_id : {
      role: `Member`,
      name: `John Doe`,
      userRef: firestore.doc.ref
    }
  }

We duplicate data for the ease of queries, etc. Most of the time users will not change their names, and organizations will not change their names.  In the event the user changes their name, we leverage a cloud function to update the appropriate documents.   We've been using this model before the arrays became more powerful. Would using arrays be better or is our model still better to use? 

I'm curious how I could manage roles per organization using the array model you described above.

jo...@nowims.com

unread,
May 1, 2019, 1:42:49 PM5/1/19
to Firebase Google Group
Kati, any advice on the above?

jo...@nowims.com

unread,
May 1, 2019, 1:43:21 PM5/1/19
to Firebase Google Group
Kato*

Kato Richardson

unread,
May 10, 2019, 12:18:51 PM5/10/19
to Firebase Google Group
Something like this would work:

match /organizations/{orgId}/documents/{docId} {
     allow read: if orgId in get(/users/$(request.auth.uid)).data.orgs;
}

Where /users/$(uid)/orgs is an array of organization ids this user belongs to.



To unsubscribe from this group and stop receiving emails from it, send an email to firebase-tal...@googlegroups.com.

To post to this group, send email to fireba...@googlegroups.com.

For more options, visit https://groups.google.com/d/optout.
Reply all
Reply to author
Forward
0 new messages