简体   繁体   中英

How to join multiple documents in a Firestore?

I have a Firestore DB with the following structure:

  • users

    • [uid]
      • name: 'User one'
  • artists

    • [uid]
      • style: 'Pop teste'
      • user_uid: [uid]

in my service I have

constructor(private afu: AngularFireAuth, private afs: AngularFirestore, private storage: AngularFireStorage) { 
   this.usersCollection = afs.collection<User>('users');
   this.users = this.usersCollection.valueChanges();
}

getUsers() {
   return this.users = this.usersCollection.snapshotChanges()
     .pipe(map(changes => {
       return changes.map(action => {
         const data = action.payload.doc.data() as User;
         return data
       });
     }));
 }

How can join between users and artists?

Using combineLatest is a great way. Since the user_uid doesn't exist on the user, I added the idField to the user as user_uid. View the code first then read below for an explanation.

    import { Component, OnInit } from '@angular/core';
    import { AngularFirestore } from '@angular/fire/firestore';
    import { Observable, combineLatest } from 'rxjs';
    interface User {
      name: string;
      user_uid: string;
    }
    interface Artist {
      style: string;
      user_uid: string;
    }
    interface Joined {
      user_uid: string;
      name: string;
      style: string;
    }
    @Component({
      selector: 'test',
      templateUrl: './test.component.html',
      styleUrls: ['./test.component.scss']
    })
    export class TestComponent implements OnInit {
    
      users$: Observable<User[]>;
      artists$: Observable<Artist[]>;
      joined$: Observable<Joined[]>;
      
      constructor(private afs: AngularFirestore){}
    
      ngOnInit(){
        this.users$ = this.afs.collection<User>('users').valueChanges({idField: 'user_uid'});
        this.artists$ = this.afs.collection<Artist>('artists').valueChanges();
        this.joined$ = combineLatest(this.users$, this.artists$, (users, artists) => {
            const joinedAsMap: Map<string, Joined> = new Map(artists.map(oneArtist =>  [oneArtist.user_uid, { ...{name: null} , ...oneArtist}]));
            users.forEach(one => joinedAsMap.set(one.user_uid , {...{name: one.name}, ...joinedAsMap.get(one.user_uid) } ));
            const joined: Joined[] = Array.from(joinedAsMap.values());
            return joined;
        });
      }
    }
  1. Make a joined interface
  2. Get both observables
  3. use combine latest
  4. Build a map with uid as key and and artist as value. Set the name to null just so the types will work. Use the spread operator to merge some objects.
  5. Loop through user and add in the user info to the value of each key
  6. Build joined array from values of map
  7. return the value

You can do this different ways but using es6 maps is a nice way to simplify some things. Also, didn't get a chance to test with a real database so you might need to verify. Also, this is all within the component for demonstration. You could do this in the service for sure.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM