Firestore pagination - Is there any query compatible with firebase's limitToLast?

前端 未结 5 1428
执念已碎
执念已碎 2020-12-18 09:46

Is there a way to implement back pagination with firestore? I am struggling to implement pagination with firestore, and there are limited firestore queries for it. Forward p

5条回答
  •  南笙
    南笙 (楼主)
    2020-12-18 09:53

    Simpler answer: Firestore now has .limitToLast(), which works exactly as you think it does. Used in my own (guess I need to publish it soon) Firestore Wrapper:

    //////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////
    // *** Paginate API ***
    
    export const PAGINATE_INIT = 0;
    export const PAGINATE_PENDING = -1;
    export const PAGINATE_UPDATED = 1;
    export const PAGINATE_DEFAULT = 10;
    export const PAGINATE_CHOICES = [10, 25, 50, 100, 250, 500];
    
    /**
     * @classdesc
     * An object to allow for paginating a table read from Firestore. REQUIRES a sorting choice
     * @property {Query} Query that forms basis for the table read
     * @property {number} limit page size
     * @property {QuerySnapshot} snapshot last successful snapshot/page fetched
     * @property {enum} status status of pagination object
     * @method PageForward pages the fetch forward
     * @method PageBack pages the fetch backward
     */
    
    export class PaginateFetch {
      Query = null;
      limit = PAGINATE_DEFAULT;
      snapshot = null;
      status = null; // -1 pending; 0 uninitialize; 1 updated;
      /**
       * ----------------------------------------------------------------------
       * @constructs PaginateFetch constructs an object to paginate through large
       * Firestore Tables
       * @param {string} table a properly formatted string representing the requested collection
       * - always an ODD number of elements
       * @param {array} filterArray an (optional) 3xn array of filter(i.e. "where") conditions
       * @param {array} sortArray a 2xn array of sort (i.e. "orderBy") conditions
       * @param {ref} ref (optional) allows "table" parameter to reference a sub-collection
       * of an existing document reference (I use a LOT of structered collections)
       *
       * The array is assumed to be sorted in the correct order -
       * i.e. filterArray[0] is added first; filterArray[length-1] last
       * returns data as an array of objects (not dissimilar to Redux State objects)
       * with both the documentID and documentReference added as fields.
       * @param {number} limit (optional)
       * @returns {PaginateFetchObject}
       **********************************************************************/
    
      constructor(
        table,
        filterArray = null,
        sortArray = null,
        ref = null,
        limit = PAGINATE_DEFAULT
      ) {
        const db = ref ? ref : fdb;
    
        this.limit = limit;
        this.Query = sortQuery(
          filterQuery(db.collection(table), filterArray),
          sortArray
        );
        this.status = PAGINATE_INIT;
      }
    
      /**
       * @method Page
       * @returns Promise of a QuerySnapshot
       */
      PageForward = () => {
        const runQuery = this.snapshot
          ? this.Query.startAfter(_.last(this.snapshot.docs))
          : this.Query;
    
        this.status = PAGINATE_PENDING;
    
        return runQuery
          .limit(this.limit)
          .get()
          .then((QuerySnapshot) => {
            this.status = PAGINATE_UPDATED;
            //*IF* documents (i.e. haven't gone beyond start)
            if (!QuerySnapshot.empty) {
              //then update document set, and execute callback
              //return Promise.resolve(QuerySnapshot);
              this.snapshot = QuerySnapshot;
            }
            return this.snapshot.docs.map((doc) => {
              return {
                ...doc.data(),
                Id: doc.id,
                ref: doc.ref
              };
            });
          });
      };
    
      PageBack = () => {
        const runQuery = this.snapshot
          ? this.Query.endBefore(this.snapshot.docs[0])
          : this.Query;
    
        this.status = PAGINATE_PENDING;
    
        return runQuery
          .limitToLast(this.limit)
          .get()
          .then((QuerySnapshot) => {
            this.status = PAGINATE_UPDATED;
            //*IF* documents (i.e. haven't gone back ebfore start)
            if (!QuerySnapshot.empty) {
              //then update document set, and execute callback
              this.snapshot = QuerySnapshot;
            }
            return this.snapshot.docs.map((doc) => {
              return {
                ...doc.data(),
                Id: doc.id,
                ref: doc.ref
              };
            });
          });
      };
    }
    
    /**
     * ----------------------------------------------------------------------
     * @function filterQuery
     * builds and returns a query built from an array of filter (i.e. "where")
     * consitions
     * @param {Query} query collectionReference or Query to build filter upong
     * @param {array} filterArray an (optional) 3xn array of filter(i.e. "where") conditions
     * @returns Firestor Query object
     */
    export const filterQuery = (query, filterArray = null) => {
      return filterArray
        ? filterArray.reduce((accQuery, filter) => {
            return accQuery.where(filter.fieldRef, filter.opStr, filter.value);
          }, query)
        : query;
    };
    
    /**
     * ----------------------------------------------------------------------
     * @function sortQuery
     * builds and returns a query built from an array of filter (i.e. "where")
     * consitions
     * @param {Query} query collectionReference or Query to build filter upong
     * @param {array} sortArray an (optional) 2xn array of sort (i.e. "orderBy") conditions
     * @returns Firestor Query object
     */
    export const sortQuery = (query, sortArray = null) => {
      return sortArray
        ? sortArray.reduce((accQuery, sortEntry) => {
            return accQuery.orderBy(sortEntry.fieldRef, sortEntry.dirStr || "asc");
            //note "||" - if dirStr is not present(i.e. falsy) default to "asc"
          }, query)
        : query;
    };
    
    

    I also have the equivalent for CollectionGroup queries, and listeners for each as well.

提交回复
热议问题