Home > Enterprise >  Angular Firestore: Map DocumentReference attribute to object while fetching collection
Angular Firestore: Map DocumentReference attribute to object while fetching collection

Time:03-12

I have an entity:

export interface FriendRequest {
    sender: Profile;
    recipient: Profile;
    isAccepted: boolean;
    dateSend: Date;
    dateAccepted?: Date;
}

This is how these etities are saved in firestore:

This is how these etities are saved in firestore

The problem: I want to fetch all documents from friend-request collection and map recipient, sender from DocumentReference type to Profile object.

I tried this. This is my method which fetches all friend-request documents:

listenOnReceivedFriendRequests(profileId: string): Observable<Array<FriendRequest>> {
        const myProfileReference = this.$db.doc(`${PROFILE_COLLECTION}/${profileId}`);

        return this.$db.collection<FriendRequest>(
            FRIEND_REQUEST_COLLECTION,
            ref => ref.where('recipient', '==', myProfileReference.ref)
        )
            .valueChanges()
            .pipe(
                map((friendRequests) => {
                    return friendRequests.map((friendRequest) => {

                        // This is the place where it goes wrong I guess
                        return {
                            ...friendRequest,
                            sender: friendRequest.sender.get().then((senderDoc) => friendRequest.sender = senderDoc.data()),
                            recipient: friendRequest.recipient.get().then((senderDoc) => friendRequest.recipient = senderDoc.data())
                        };
                    });

                }),
                tap(result => console.log(result))
            );
    }

But it returns:

[
    {
        sender: ZoneAwarePromise,       // <-- Instead of Profile object
        recipient: ZoneAwarePromise,    // <-- Instead of Profile object
        isAccepted: ...,
        dateSend: ...,
        dateAccepted: ...,
    },
    {
        sender: ZoneAwarePromise,       // <-- Instead of Profile object
        recipient: ZoneAwarePromise,    // <-- Instead of Profile object
        isAccepted: ...,
        dateSend: ...,
        dateAccepted: ...,
    },
]

Instead of my required output:

[
    {
        sender: Profile,
        recipient: Profile,
        isAccepted: ...,
        dateSend: ...,
        dateAccepted: ...,
    },
    {
        sender: Profile,
        recipient: Profile,
        isAccepted: ...,
        dateSend: ...,
        dateAccepted: ...,
    },
]

I know I should wait until promises of sender and recipient finish, but I dont know how to do it to return my required output.

CodePudding user response:

Instead of the map operator, you can handle it like the following:

  • Use one of the higher-order RxJS mapping operators, to map each one of the FriendRequest to an Observable.
  • Each Observable will fetch the related sender and recipient by combining the two Promise(s) to one Observable using forkJoin function (after converting each one of the Promise(s) to an Observable using from function)
  • Then map each Observable result again to a FriendRequest object.
  • Wrap the result array of Observable with forkJoin function, to return in the end Array<FriendRequest.

Try something like the following:

// import { forkJoin, from, Observable } from 'rxjs';
// import { map, mergeMap } from 'rxjs/operators';

listenOnReceivedFriendRequests(
  profileId: string
): Observable<Array<FriendRequest>> {
  const myProfileReference = this.$db.doc(
    `${PROFILE_COLLECTION}/${profileId}`
  );

  return this.$db
    .collection<FriendRequest>(FRIEND_REQUEST_COLLECTION, (ref) =>
      ref.where('recipient', '==', myProfileReference.ref)
    )
    .valueChanges()
    .pipe(
      mergeMap((friendRequests: Array<FriendRequest>) =>
        // forkJoin returns Observable<Array<FriendRequest>>
        forkJoin(
          // map each item to an Observable<FriendRequest>, after resolving the related profiles.
          friendRequests.map((friendRequest) =>
            forkJoin({
              senderDoc: from(friendRequest.sender.get()),
              recipientDoc: from(friendRequest.recipient.get()),
            }).pipe(
              map(({ senderDoc, recipientDoc }) => ({
                ...friendRequest,
                sender: senderDoc.data(),
                recipient: recipientDoc.data(),
              }))
            )
          )
        )
      )
    );
}
  • Related