github.com/RevenueMonster/sqlike@v1.0.6/README.md (about)

     1  # sqlike
     2  
     3  [![Build](https://github.com/RevenueMonster/sqlike/workflows/build/badge.svg?branch=main)](https://github.com/RevenueMonster/sqlike/actions?query=workflow%3Abuild)
     4  [![Release](https://img.shields.io/github/v/tag/si3nloong/sqlike)](https://github.com/RevenueMonster/sqlike/releases)
     5  [![Go Report](https://goreportcard.com/badge/github.com/RevenueMonster/sqlike)](https://goreportcard.com/report/github.com/RevenueMonster/sqlike)
     6  [![Go Coverage](https://codecov.io/gh/si3nloong/sqlike/branch/main/graph/badge.svg)](https://codecov.io/gh/si3nloong/sqlike)
     7  [![LICENSE](https://img.shields.io/github/license/si3nloong/sqlike)](https://github.com/RevenueMonster/sqlike/blob/main/LICENSE)
     8  [![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Fsi3nloong%2Fsqlike.svg?type=shield)](https://app.fossa.com/projects/git%2Bgithub.com%2Fsi3nloong%2Fsqlike?ref=badge_shield)
     9  
    10  > A Golang SQL ORM which anti-toxic query and focus on the latest features.
    11  
    12  ## 🔨 Installation
    13  
    14  ```console
    15  go get github.com/RevenueMonster/sqlike
    16  ```
    17  
    18  Fully compatible with native library `database/sql`, which mean you are allow to use `driver.Valuer` and `sql.Scanner`.
    19  
    20  ## 📻 Legacy Support
    21  
    22  SQLike did support **mysql 5.7** as well. For better compatibility, we suggest you to use at least mysql 8.0.
    23  
    24  ## 🪣 Minimum Requirements
    25  
    26  - **mysql 8.0** and above
    27  - **golang 1.15** and above
    28  
    29  ## ❓ Why another ORM?
    30  
    31  - We don't really care about _legacy support_, we want _latest feature_ that mysql and golang offer us
    32  - We want to get rid from **toxic query** (also known as slow query)
    33  
    34  ## ✨ What do we provide apart from native package (database/sql)?
    35  
    36  - Support `ENUM` and `SET`
    37  - Support `UUID` (^8.0)
    38  - Support `JSON`
    39  - Support `descending index` (^8.0)
    40  - Support `multi-valued` index (^8.0.17)
    41  - Support `Spatial` with package [orb](https://github.com/paulmach/orb), such as `Point`, `LineString`
    42  - Support `generated column` of `stored column` and `virtual column`
    43  - Extra custom type such as `Date`, `Key`, `Boolean`
    44  - Support `struct` on `Find`, `FindOne`, `InsertOne`, `Insert`, `ModifyOne`, `DeleteOne`, `Delete`, `DestroyOne` and `Paginate` apis
    45  - Support `Transactions`
    46  - Support cursor based pagination
    47  - Support advance and complex query statement
    48  - Support [civil.Date](https://cloud.google.com/go/civil#Date), [civil.Time](https://cloud.google.com/go/civil#Time) and [time.Location](https://pkg.go.dev/time#Time)
    49  - Support [language.Tag](https://godoc.org/golang.org/x/text/language#example-Tag--Values) and [currency.Unit](https://godoc.org/golang.org/x/text/currency#Unit)
    50  - Support authorization plugin [Casbin](https://github.com/casbin/casbin)
    51  - Support tracing plugin [OpenTracing](https://github.com/opentracing/opentracing-go)
    52  - Developer friendly, (query is highly similar to native sql query)
    53  - Support `sqldump` for backup purpose **(experiment)**
    54  
    55  <!-- You can refer to [examples](https://github.com/RevenueMonster/sqlike/tree/main/examples) folder to see what apis we offer and learn how to use those apis -->
    56  
    57  ## ⚠️ Limitation
    58  
    59  Our main objective is anti toxic query, that why some functionality we doesn't offer out of box
    60  
    61  - offset based pagination (but you may achieve this by using `Limit` and `Offset`)
    62  - eager loading (we want to avoid magic function, you should handle this by your own using goroutines)
    63  - join (eg. left join, outer join, inner join), join clause is consider as toxic query, you should alway find your record using primary key
    64  - left wildcard search using Like is not allow (but you may use `expr.Raw` to bypass it)
    65  - bidirectional sorting is not allow (except mysql 8.0 and above)
    66  - currently only support `mysql` driver (postgres and sqlite yet to implement)
    67  
    68  ## General APIs
    69  
    70  ```go
    71  package main
    72  
    73  import (
    74      "time"
    75      "github.com/RevenueMonster/sqlike/sqlike/actions"
    76      "github.com/RevenueMonster/sqlike/sqlike"
    77      "github.com/RevenueMonster/sqlike/sqlike/options"
    78      "github.com/RevenueMonster/sqlike/sql/expr"
    79      "github.com/google/uuid"
    80      "context"
    81  
    82      _ "github.com/go-sql-driver/mysql"
    83  )
    84  
    85  // UserStatus :
    86  type UserStatus string
    87  
    88  const (
    89      UserStatusActive  UserStatus = "ACTIVE"
    90      UserStatusSuspend UserStatus = "SUSPEND"
    91  )
    92  
    93  type User struct {
    94      ID        uuid.UUID  `sqlike:",primary_key"`
    95      ICNo      string     `sqlike:",generated_column"` // generated column generated by virtual column `Detail.ICNo`
    96      Name      string     `sqlike:",size=200,charset=latin1"` // you can set the data type length and charset with struct tag
    97      Email     string     `sqlike:",unique"` // set to unique
    98      Address   string     `sqlike:",longtext"` // `longtext` is an alias of long text data type in mysql
    99      Detail    struct {
   100          ICNo    string `sqlike:",virtual_column=ICNo"` // virtual column
   101          PhoneNo string
   102          Age     uint
   103      }
   104      Status    UserStatus `sqlike:",enum=ACTIVE|SUSPEND"` // enum data type
   105      CreatedAt time.Time
   106      UpdatedAt time.Time
   107  }
   108  
   109  func newUser() (user User) {
   110      now := time.Now()
   111      user.ID = uuid.New()
   112      user.CreatedAt = now
   113      user.UpdatedAt = now
   114      return
   115  }
   116  
   117  func main() {
   118      ctx := context.Background()
   119      client := sqlike.MustConnect(
   120          ctx,
   121          "mysql",
   122          options.Connect().
   123          SetUsername("root").
   124          SetPassword("").
   125          SetHost("localhost").
   126          SetPort("3306"),
   127      )
   128  
   129      client.SetPrimaryKey("ID") // Change default primary key name
   130      version := client.Version() // Display driver version
   131      dbs, _ := client.ListDatabases(ctx) // List databases
   132  
   133      userTable := client.Database("sqlike").Table("User")
   134  
   135      // Drop Table
   136      userTable.Drop(ctx)
   137  
   138      // Migrate Table
   139      userTable.Migrate(ctx, User{})
   140  
   141      // Truncate Table
   142      userTable.Truncate(ctx)
   143  
   144      // Insert one record
   145      {
   146          user := newUser()
   147          if _, err := userTable.InsertOne(ctx, &user); err != nil {
   148              panic(err)
   149          }
   150      }
   151  
   152      // Insert multiple record
   153      {
   154          users := [...]User{
   155              newUser(),
   156              newUser(),
   157              newUser(),
   158          }
   159          if _, err := userTable.Insert(ctx, &users); err != nil {
   160              panic(err)
   161          }
   162      }
   163  
   164      // Find one record
   165      {
   166          user := User{}
   167          err := userTable.FindOne(ctx, nil).Decode(&user)
   168          if err != nil {
   169              // `sqlike.ErrNoRows` is an alias of `sql.ErrNoRows`
   170              if err != sqlike.ErrNoRows {  // or you may check with sql.ErrNoRows
   171                  panic(err)
   172              }
   173              // record not exist
   174          }
   175      }
   176  
   177      // Find multiple records
   178      {
   179          users := make([]User, 0)
   180          result, err := userTable.Find(
   181              ctx,
   182              actions.Find().
   183                  Where(
   184                      expr.Equal("ID", result.ID),
   185                  ).
   186                  OrderBy(
   187                      expr.Desc("UpdatedAt"),
   188                  ),
   189          )
   190          if err != nil {
   191              panic(err)
   192          }
   193          // map into the struct of slice
   194          if err:= result.All(&users); err != nil {
   195              panic(err)
   196          }
   197      }
   198  
   199      // Update one record with all fields of struct
   200      {
   201          user.Name = `🤖 Hello World!`
   202          if err := userTable.ModifyOne(ctx, &user); err != nil {
   203              panic(err)
   204          }
   205      }
   206  
   207      // Update one record with selected fields
   208      {
   209          userTable.UpdateOne(
   210              ctx,
   211              actions.UpdateOne().
   212                  Where(
   213                      expr.Equal("ID", 100),
   214                  ).Set(
   215                      expr.ColumnValue("Name", "SianLoong"),
   216                      expr.ColumnValue("Email", "test@gmail.com"),
   217                  ),
   218              options.UpdateOne().SetDebug(true), // debug the query
   219          )
   220      }
   221  
   222      {
   223          limit := uint(10)
   224          pg, err := userTable.Paginate(
   225              ctx,
   226              actions.Paginate().
   227                  OrderBy(
   228                      expr.Desc("CreatedAt"),
   229                  ).
   230                  Limit(limit + 1),
   231               options.Paginate().SetDebug(true),
   232          )
   233          if err != nil {
   234              panic(err)
   235          }
   236  
   237          for {
   238              var users []User
   239              if err := pg.All(&users); err != nil {
   240                  panic(err)
   241              }
   242              length := uint(len(users))
   243              if length == 0 {
   244                  break
   245              }
   246              cursor := users[length-1].ID
   247              if err := pg.NextCursor(ctx, cursor); err != nil {
   248                  if err == sqlike.ErrInvalidCursor {
   249                      break
   250                  }
   251                  panic(err)
   252              }
   253              if length <= limit {
   254                  break
   255              }
   256          }
   257  
   258      }
   259  }
   260  ```
   261  
   262  Inspired by [gorm](https://github.com/jinzhu/gorm), [mongodb-go-driver](https://github.com/mongodb/mongo-go-driver) and [sqlx](https://github.com/jmoiron/sqlx).
   263  
   264  ## 🎉 Big Thanks To
   265  
   266  Thanks to these awesome companies for their support of Open Source developers ❤
   267  
   268  [![GitHub](https://jstools.dev/img/badges/github.svg)](https://github.com/open-source)
   269  [![NPM](https://jstools.dev/img/badges/npm.svg)](https://www.npmjs.com/)
   270  
   271  ## 🧲 Adopters 
   272  
   273  <img src="https://camo.githubusercontent.com/2c975785e2902b4ca4d1a9e9fd4bd21b3e576395631f1dc7c49f170cdeb7f1f8/68747470733a2f2f61737365742e77657469782e6d792f696d616765732f6c6f676f2f77657469782e706e67"
   274  width="120px" style="margin-right: 10px" />
   275  <img src="https://user-images.githubusercontent.com/28108597/188676138-59b5fa89-9788-48ce-9a12-bdcc378bb8b7.png" width="150px" style="margin-right: 10px" />
   276  <img src="https://user-images.githubusercontent.com/28108597/188675656-af6fb714-0460-40b3-8935-f0eeaa0886b1.png" width="130px" style="margin-right: 10px" />
   277  <img src="https://user-images.githubusercontent.com/28108597/188676512-59a6f5bd-5c57-4051-bbd9-9275402b415f.png" width="70px" />
   278  
   279  ## 📄 License
   280  
   281  [MIT](https://github.com/RevenueMonster/sqlike/blob/main/LICENSE)
   282  
   283  Copyright (c) 2019-present, SianLoong Lee
   284  
   285  [![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Fsi3nloong%2Fsqlike.svg?type=large)](https://app.fossa.com/projects/git%2Bgithub.com%2Fsi3nloong%2Fsqlike?ref=badge_large)