0

I've the following structure in my firebase DB:

/profiles/{uid}/displayName
               /email
               /otherAttribues
               /roles/{roleName}/someAttribute
                                /someOtherAttribute

So "roles" is a subcollection, mostly because it has to have different writes rights than the rest of the profile.

In my component, I currently retrieve the profiles:

  constructor(private firestore: AngularFirestore) {
    this.users$ = this.firestore.collection<User>('profiles').valueChanges();
  }

In my html, I'm using this users$ in a *ngFor loop to display the profiles in the grid. I need to add one column that will be shown based on the presence/absence of a role with the ID "admin" or "user". This can be modified from other users, so it has to be an observable.

How can I retrieve the "roles" of my profile(at least the IDs of the documents in the subcollection) and still iterate on them nicely?

This can change, so I need this to be an observable. I cannot find a way to "include" a subcollection in my request.

And based on the roles, I need to have

Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
J4N
  • 19,480
  • 39
  • 187
  • 340

2 Answers2

1

Firestore reads are shallow, they only read from the collection (group) that you name, so from user in your case. There is no way to also read from the subcollection in the same operation.

There are two common options to information about the user's roles:

  1. Perform an extra read operation, either for each individual user, or by a collection group query for all roles collections in one go.
  2. By duplicating the relevant information from roles into the parent document in user.

Given how simple your query requirement is, I'd likely choose to (also) store the information in the user document, either from the client directly, or through a Cloud Function that is triggered whenever the roles data is updated.

Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
  • 1. How can you do a group query to consolidate each object with the subcollection? 2. I cannot duplicate, they do not have the same write permissions, so it would mean that an user could promotes himself admin for some features – J4N Aug 26 '21 at 14:15
  • 2) You could reject changes to that specific field in the security rules. 1) I'm not sure I understand. If you're wondering how to determine what user document a certain role document belong to, that'd be a matter of following the chain of `parent` properties upwards similar to what I show here for Flutter: https://stackoverflow.com/a/61713774. – Frank van Puffelen Aug 26 '21 at 14:33
  • Hi @Frank, thank you for your answer, but I must admit your answer wasn't really usefull, since it was in a different collection, it was kind of obvious that this was needing an extra needs, I just didn't know how to do this with map/tap/switchmap/combineLatest operators. Kinda new with the RXJS world, I'm much more used to the async/await patterns – J4N Sep 06 '21 at 04:53
1

I've done the following in my user component:

this.users$ = this.firestore.collection<User>('profiles').valueChanges({ idField: 'uid' }).pipe(
      switchMap((profiles) => {
        const res = profiles.map(profile => {
          return this.firestore.collection(`profiles/${profile.uid}/roles`).valueChanges({ idField: 'Id' }).pipe(
            map(doc => {
              return doc.map(d => d.Id)
            }),
            tap(roles => {
              //TODO: consolidate the profile with the roles here
              //This will be updated on changes
            }),
            map(roles => Object.assign(profile, { roles }))
          );
        })
        return combineLatest(...res);
      })
    );

Note: it warns me about the deprecation of combineLatest, but not sure how to replace it.

J4N
  • 19,480
  • 39
  • 187
  • 340