'Firebase filter with pagination

I'm doing an opensource Yelp with Firebase + Angular.

My database:

    {
      "reviews" : {
        "-L0f3Bdjk9aVFtVZYteC" : {
          "comment" : "my comment",
          "ownerID" : "Kug2pR1z3LMcZbusqfyNPCqlyHI2",
          "ownerName" : "MyName",
          "rating" : 2,
          "storeID" : "-L0e8Ua03XFG9k0zPmz-"
        },
        "-L0f7eUGqenqAPC1liYj" : {
          "comment" : "me second comment",
          "ownerID" : "Kug2pR1z3LMcZbusqfyNPCqlyHI2",
          "ownerName" : "MyName",
          "rating" : 3,
          "storeID" : "-L0e8Ua03XFG9k0zPmz-"
        },    
      },
      "stores" : {
        "-L0e8Ua03XFG9k0zPmz-" : {      
          "description" : "My good Store",      
          "name" : "GoodStore",
          "ownerID" : "39UApyo0HIXmKPrTOi8D0nWLi6n2",      
          "tags" : [ "good", "health", "cheap" ],
        }
      },
      "users" : {
        "39UApyo0HIXmKPrTOi8D0nWLi6n2" : {
          "name" : "First User"
        },
        "Kug2pR1z3LMcZbusqfyNPCqlyHI2" : {
          "name" : "MyName",
          "reviews" : {
            "-L0f3Bdjk9aVFtVZYteC" : true,
            "-L0f7eUGqenqAPC1liYj" : true
          }
        }
      }
    }

I use this code below to get all store's reviews (using AngularFire2)

    getReviews(storeID: string){
        return this.db.list('/reviews', ref => { 
            return ref.orderByChild('storeID').equalTo(storeID);
        });
    }

Now, I want to make a server-side review pagination, but I think I cannot do it with this database structure. Am I right? Tried:

    getReviews(storeID: string){
        return this.db.list('/reviews', ref => { 
            return ref.orderByChild('storeID').equalTo(storeID).limitToLast(10) //How to make pagination without retrive all data?
        });
    }

I thought that I could put all reviews inside stores, but (i) I don't want to retrieve all reviews at once when someone ask for a store and (ii) my review has a username, so I want to make it easy to change it (that why I have a denormalized table)



Solution 1:[1]

Refer this and this documentation.

For the first page:

snapshot = await ref.orderByChild('storeID')
              .equalTo(store_id) //store_id is the variable name.
              .limitToLast(10)
              .once("value")

Store the firstKey (NOT the lastKey) from the above query. (Since you are using limitToLast())

firstKey = null
snapshot.forEach(snap => {
    if (!firstKey)
        firstKey = snap.key
    // code
})

For the next page:

snapshot = await ref.orderByChild('storeID') //storeID is the field name in the database
                    .startAt(store_id) //store_id is the variable name which has the desired store ID
                    .endAt(store_id, firstKey)
                    .limitToLast(10 + 1) //1 is added because you will also get value for the firstKey
                    .once("value")

The above query will fetch 11 list data which will contain one redundant data from the first page's query.

How it works:

startAt ( value : number | string | boolean | null , key ? : string ) : Query

The starting point is inclusive, so children with exactly the specified value will be included in the query. The optional key argument can be used to further limit the range of the query. If it is specified, then children that have exactly the specified value must also have a key name greater than or equal to the specified key.

endAt ( value : number | string | boolean | null , key ? : string ) : Query

The ending point is inclusive, so children with exactly the specified value will be included in the query. The optional key argument can be used to further limit the range of the query. If it is specified, then children that have exactly the specified value must also have a key name less than or equal to the specified key.

So the query will try to fetch:

storeID >= store_id && storeID <= store_id (lexicographically)

which will equal to

storeID == store_id

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Sagar Mc