프로그래밍 & IT 정보/Etc.

[Firebase/Angular] Cloud Firestore Pagination Query Example

아미넴 2020. 7. 3.
반응형

Angular에서 Firebase Cloud Firestore DB 데이터 조회 시 Paging 처리를 좀 더 효과적으로 하고자 할 때 참고 바랍니다.

 

 

import { AngularFirestore } from '@angular/fire/firestore';
import { first } from 'rxjs/operators';
import * as firebase from 'firebase';

export const dbRootPath: string = "rootPath";

// 조회 타입 정의
enum queryType { "init", "after" };
type QueryType = keyof typeof queryType;

// 기본 셋팅 정의
export interface QueryConfig {
    type: QueryType,
    path: string,
    orderByField?: string,
    reverse?: boolean,
    limit?: number,
    page?: string
}

// 조건절 정의
export interface WhereConfig {
    field: string[],
    value: any[],
    operator: firebase.firestore.WhereFilterOp[]
}

export class FirebasePagination {
    constructor(private afs: AngularFirestore) { }

    lastVisible: {
        page: string,
        doc: firebase.firestore.QueryDocumentSnapshot
    }[] = [];

    // 데이터 조회 (페이징 처리)
    async paginationQuery(queryConfig: QueryConfig, whereConfig?: WhereConfig) {
        try {
            const lastVisibleIndex: number = this.lastVisible.findIndex(data => data.page === queryConfig.page);

            const result = await this.afs.collection<any>(dbRootPath + '/' + queryConfig.path, (ref) => {
                let queryRef: firebase.firestore.Query = ref;

                if (queryConfig.orderByField) {
                    queryRef = queryRef.orderBy(queryConfig.orderByField, queryConfig.reverse ? 'desc' : 'asc');
                }

                if (whereConfig && whereConfig.field.length > 0) {
                    for (let i: number = 0; i < whereConfig.field.length; i++) {
                        queryRef = queryRef.where(whereConfig.field[i], whereConfig.operator[i], whereConfig.value[i]);
                    }
                }

                if (queryConfig.limit) {
                    queryRef = queryRef.limit(queryConfig.limit);
                }

                // 조회 타입이 after일 경우 적용
                if (queryConfig.type === "after" && lastVisibleIndex >= 0) {
                    queryRef = queryRef.startAfter(this.lastVisible[lastVisibleIndex].doc);
                }

                return queryRef
            }).get().pipe(first()).toPromise();

            // 조회 데이터 마지막 값 저장
            if (result.docs.length > 0) {
                if (lastVisibleIndex >= 0) {
                    this.lastVisible[lastVisibleIndex] = {
                        page: queryConfig.page,
                        doc: result.docs[result.docs.length - 1]
                    };
                } else {
                    this.lastVisible.push({
                        page: queryConfig.page,
                        doc: result.docs[result.docs.length - 1]
                    });
                }
            }

            const resultArr: any[] = [];

            result.docs.forEach(item => {
                let _item: any = item.data();
                _item.docId = item.id; // id 값 포함시키기 위함
                resultArr.push(_item);
            });

            return resultArr;
        } catch (error) {
            throw error;
        }
    }
}
반응형

댓글

💲 추천 글