Code Monkey home page Code Monkey logo

gorm-cursor-paginator's Introduction

gorm-cursor-paginator Build Status Coverage Status Go Report Card Codacy Badge

A paginator doing cursor-based pagination based on GORM

This doc is for v2, which uses GORM v2. If you are using GORM v1, please checkout v1 doc.

Features

  • Query extendable.
  • Multiple paging keys.
  • Paging rule customization (e.g., order, SQL representation) for each key.
  • GORM column tag supported.
  • Error handling enhancement.
  • Exporting cursor module for advanced usage.

Installation

go get -u github.com/hashicorp/gorm-cursor-paginator/v2

Usage By Example

Given a User model:

type User struct {
    ID          int
    JoinedAt    time.Time `gorm:"column:created_at"`
}

We need to construct a paginator.Paginator based on fields of User struct. First we import paginator:

import (
   "github.com/hashicorp/gorm-cursor-paginator/v2/paginator"
)

Then we can start configuring paginator.Paginator, here are some useful patterns:

// configure paginator with paginator.Config and paginator.Option
func UserPaginator(
    cursor paginator.Cursor, 
    order *paginator.Order,
    limit *int,
) *paginator.Paginator {
    opts := []paginator.Option{
        &paginator.Config{
            // keys should be ordered by ordering priority
            Keys: []string{"ID", "JoinedAt"}, // default: []string{"ID"}
            Limit: 5, // default: 10
            Order: paginator.ASC, // default: DESC
        },
    }
    if limit != nil {
        opts = append(opts, paginator.WithLimit(*limit))
    }
    if order != nil {
        opts = append(opts, paginator.WithOrder(*order))
    }
    if cursor.After != nil {
        opts = append(opts, paginator.WithAfter(*cursor.After))
    }
    if cursor.Before != nil {
        opts = append(opts, paginator.WithBefore(*cursor.Before))
    }
    return paginator.New(opts...)
}

// configure paginator with setters
func UserPaginator(
    cursor paginator.Cursor,
    order *paginator.Order, 
    limit *int,
) *paginator.Paginator {
    p := paginator.New(
        paginator.WithKeys("ID", "JoinedAt"),
        paginator.WithLimit(5),
        paginator.WithOrder(paginator.ASC),
    )
    if order != nil {
        p.SetOrder(*order)
    }
    if limit != nil {
        p.SetLimit(*limit)
    }
    if cursor.After != nil {
        p.SetAfter(*cursor.After)
    }
    if cursor.Before != nil {
        p.SetBefore(*cursor.Before)
    }
    return p
}

If you need fine grained setting for each key, you can use paginator.Rule:

SQLRepr is especially useful when you have JOIN or table alias in your SQL query. If SQLRepr is not specified, paginator will use the table name from paginated model, plus table key derived by below rules to form the SQL query:

  1. Search GORM tag column on struct field.
  2. If tag not found, convert struct field name to snake case.
func UserPaginator(/* ... */) {
    opts := []paginator.Option{
        &paginator.Config{
            Rules: []paginator.Rule{
                {
                    Key: "ID",
                },
                {
                    Key: "JoinedAt",
                    Order: paginator.ASC,
                    SQLRepr: "users.created_at",
                },
            },
            Limit: 5,
            Order: paginator.DESC, // outer order will apply to keys without order specified, in this example is the key "ID".
        },
    }
    // ...
    return paginator.New(opts...)
}

After setup, you can start paginating with GORM:

func FindUsers(db *gorm.DB, query Query) ([]User, paginator.Cursor, error) {
    var users []User

    // extend query before paginating
    stmt := db.
        Select(/* fields */).
        Joins(/* joins */).
        Where(/* queries */)

    // find users with pagination
    result, cursor, err := UserPaginator(/* config */).Paginate(stmt, &users)

    // this is paginator error, e.g., invalid cursor
    if err != nil {
        return nil, paginator.Cursor{}, err
    }

    // this is gorm error
    if result.Error != nil {
        return nil, paginator.Cursor{}, result.Error
    }

    return users, cursor, nil
}

The second value returned from paginator.Paginator.Paginate is a paginator.Cursor, which is a re-exported struct from cursor.Cursor:

type Cursor struct {
    After  *string `json:"after" query:"after"`
    Before *string `json:"before" query:"before"`
}

That's all! Enjoy paginating in the GORM world. ๐ŸŽ‰

For more paginating examples, please checkout exmaple/main.go and paginator/paginator_paginate_test.go

For manually encoding/decoding cursor exmaples, please checkout cursor/encoding_test.go

Known Issues

  1. Please make sure you're not paginating by nullable fields. Nullable values would occur NULLS { FIRST | LAST } problems. Current workaround recommended is to select only non-null fields for paginating, or filter null values beforehand:

    stmt = db.Where("nullable_field IS NOT NULL")

License

ยฉ Cyan Ho (pilagod), 2018-NOW

Released under the MIT License

gorm-cursor-paginator's People

Contributors

codacy-badger avatar dadgar avatar pilagod avatar pratikmallya avatar sylviamoss avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.