Skip to content
This repository has been archived by the owner on Nov 6, 2021. It is now read-only.

Pagination

B1nj0y edited this page May 5, 2018 · 38 revisions

A keyset style pagination

100% offset-free

What's a keyset pagination: http://use-the-index-luke.com/no-offset.

Basic Usage

Take a model named Post as an example. A data type PostPage will be generated for paginating:

type PostPage struct {
    WhereString string
    WhereParams []interface{}
    Order       map[string]string
    FirstId     int64
    LastId      int64
    PageNum     int
    PerPage     int
    TotalPages  int
    TotalItems  int64
}

And meanwhile generated three methods Current, Next and Previous:

// Current get the current page of PostPage object for pagination
func (_p *PostPage) Current() ([]Post, error)

// Current get the next page of PostPage object for pagination
func (_p *PostPage) Next() ([]Post, error)

// Current get the previous page of PostPage object for pagination 
func (_p *PostPage) Previous() ([]Post, error)

You can init a PostPage at first:

pp := &PostPage{Order: map[string]string{"id": "desc"}, PerPage: 5}

Then get the first page:

ps, err := pp.Current()

Then next page:

ps, err = pp.Next()

Or back to previous page:

ps, err = pp.Previous()

Show the current page number:

fmt.Println(pp.PageNum)

Show total pages and total count:

fmt.Println(pp.TotalPages)
fmt.Println(pp.TotalItems)

Add a where clause

You can add a where clause with parameters as a filter when init a PostPage object:

pp := &PostPage{
WhereString: "title LIKE ? AND hits > ?", 
WhereParams: []interface{}{"%go-on-rails%", 200}, 
Order: map[string]string{"id": "desc"}, 
PerPage: 5}

Here the WhereString is a ? binding with WhereParams correspondingly.

A sample code in a Controller(Handler function)

All above is making a pagination object in the Model level, now we show how to make it work from the View to the Controller(a handler), and then call the methods of the object.

For example we GET an index page and pass three parameters:

  • direction: previous / current / next, required
  • first_id: the first ID in the page, optional, it will be 0 if omited
  • last_id: the last ID in the page, optional, it will be 0 if omited

In our Controller, we will do some check and then create a pagination object to get a page of records. Here we show how to make it in the Gin framework:

function IndexHandler(c *gin.Context) {
    direction := c.Param("direction")
    firstId, _ := strconv.ParseInt(c.Param("first_id"), 10, 64)
    lastId, _ := strconv.ParseInt(c.Param("last_id"), 10, 64)
    
    pp := &PostPage{
        WhereString: "title LIKE ? AND hits > ?", 
        WhereParams: []interface{}{"%go-on-rails%", 200}, 
        Order: map[string]string{"id": "desc"}, 
        PerPage: 5,
        FirstId: firstId,
        LastId: lastId}

    ps := []Post
    switch direction {
        case "previous":
            ps, _ = pp.Previous()
        case "next":
            ps, _ = pp.Next()
        default:
            ps, _ = pp.Current()
    }

    c.JSON(200, gin.H{
			"status":  "success"
			"data": ps
		})
}

maybe you'll think the switch loop is tedious to write for every pagination controller, so there's a helper function for you: GetPage. We can rewrite switch section:

function IndexHandler(c *gin.Context) {
    direction := c.Param("direction")
    firstId, _ := strconv.ParseInt(c.Param("first_id"), 10, 64)
    lastId, _ := strconv.ParseInt(c.Param("last_id"), 10, 64)
    
    pp := &PostPage{
        WhereString: "title LIKE ? AND hits > ?", 
        WhereParams: []interface{}{"%go-on-rails%", 200}, 
        Order: map[string]string{"id": "desc"}, 
        PerPage: 5,
        FirstId: firstId,
        LastId: lastId}

    ps, err := pp.GetPage(direction)

    c.JSON(200, gin.H{
			"status":  "success"
			"data": ps
		})
}

it gets a little bit clearer!

How to build a url in a View?

In the View, take the GET action for example, the relative path will be:

/posts?direction=(previous|current|next)&first_id=<PAGE_NUM>&last_id=<PAGE_NUM>

Other actions like POST are similar to pass the three parameters: direction, first_id and last_id.