github.com/unionj-cloud/go-doudou@v1.3.8-0.20221011095552-0088008e5b31/toolkit/sqlext/query/query.go (about)

     1  package query
     2  
     3  import (
     4  	"fmt"
     5  	"github.com/unionj-cloud/go-doudou/toolkit/sqlext/arithsymbol"
     6  	"github.com/unionj-cloud/go-doudou/toolkit/sqlext/logicsymbol"
     7  	"github.com/unionj-cloud/go-doudou/toolkit/sqlext/sortenum"
     8  	"github.com/unionj-cloud/go-doudou/toolkit/stringutils"
     9  	"reflect"
    10  	"strings"
    11  )
    12  
    13  // Base sql expression
    14  type Base interface {
    15  	Sql() (string, []interface{})
    16  	//NamedSql() (string, []interface{})
    17  }
    18  
    19  // Q used for building sql expression
    20  type Q interface {
    21  	Base
    22  	And(q Base) Where
    23  	Or(q Base) Where
    24  	Append(q Base) Where
    25  	End(q Base) Where
    26  }
    27  
    28  // Criteria wrap a group of column, value and operator such as name = 20
    29  type Criteria struct {
    30  	// table alias
    31  	talias string
    32  	col    string
    33  	val    interface{}
    34  	asym   arithsymbol.ArithSymbol
    35  }
    36  
    37  // Sql implement Base interface, return sql expression
    38  func (c Criteria) Sql() (string, []interface{}) {
    39  	if c.asym == arithsymbol.In || c.asym == arithsymbol.NotIn {
    40  		var args []interface{}
    41  		var sb strings.Builder
    42  		sb.WriteString(fmt.Sprintf("`%s` %s (", c.col, c.asym))
    43  
    44  		var vals []string
    45  		switch reflect.TypeOf(c.val).Kind() {
    46  		case reflect.Slice:
    47  			data := reflect.ValueOf(c.val)
    48  			for i := 0; i < data.Len(); i++ {
    49  				vals = append(vals, "?")
    50  				args = append(args, data.Index(i).Interface())
    51  			}
    52  		default:
    53  			vals = append(vals, "?")
    54  			args = append(args, c.val)
    55  		}
    56  
    57  		sb.WriteString(strings.Join(vals, ","))
    58  		sb.WriteString(")")
    59  
    60  		return sb.String(), args
    61  	}
    62  	if stringutils.IsNotEmpty(c.talias) {
    63  		if c.asym == arithsymbol.Is || c.asym == arithsymbol.Not {
    64  			return fmt.Sprintf("%s.`%s` %s null", c.talias, c.col, c.asym), nil
    65  		}
    66  		return fmt.Sprintf("%s.`%s` %s ?", c.talias, c.col, c.asym), []interface{}{c.val}
    67  	}
    68  	if c.asym == arithsymbol.Is || c.asym == arithsymbol.Not {
    69  		return fmt.Sprintf("`%s` %s null", c.col, c.asym), nil
    70  	}
    71  	return fmt.Sprintf("`%s` %s ?", c.col, c.asym), []interface{}{c.val}
    72  }
    73  
    74  // C new a Criteria
    75  func C() Criteria {
    76  	return Criteria{}
    77  }
    78  
    79  func (c Criteria) ToWhere() Where {
    80  	w := Where{
    81  		children: make([]Base, 0),
    82  	}
    83  	w.children = append(w.children, c, String(""))
    84  	w.lsym = logicsymbol.End
    85  	return w
    86  }
    87  
    88  // Col set column name
    89  func (c Criteria) Col(col string) Criteria {
    90  	if strings.Contains(col, ".") {
    91  		i := strings.Index(col, ".")
    92  		c.talias = col[:i]
    93  		c.col = col[i+1:]
    94  	} else {
    95  		c.col = col
    96  	}
    97  	return c
    98  }
    99  
   100  // Eq set = operator and column value
   101  func (c Criteria) Eq(val interface{}) Criteria {
   102  	c.val = val
   103  	c.asym = arithsymbol.Eq
   104  	return c
   105  }
   106  
   107  // Ne set != operator and column value
   108  func (c Criteria) Ne(val interface{}) Criteria {
   109  	c.val = val
   110  	c.asym = arithsymbol.Ne
   111  	return c
   112  }
   113  
   114  // Gt set > operator and column value
   115  func (c Criteria) Gt(val interface{}) Criteria {
   116  	c.val = val
   117  	c.asym = arithsymbol.Gt
   118  	return c
   119  }
   120  
   121  // Lt set < operator and column value
   122  func (c Criteria) Lt(val interface{}) Criteria {
   123  	c.val = val
   124  	c.asym = arithsymbol.Lt
   125  	return c
   126  }
   127  
   128  // Gte set >= operator and column value
   129  func (c Criteria) Gte(val interface{}) Criteria {
   130  	c.val = val
   131  	c.asym = arithsymbol.Gte
   132  	return c
   133  }
   134  
   135  // Lte set <= operator and column value
   136  func (c Criteria) Lte(val interface{}) Criteria {
   137  	c.val = val
   138  	c.asym = arithsymbol.Lte
   139  	return c
   140  }
   141  
   142  // IsNull set is null
   143  func (c Criteria) IsNull() Criteria {
   144  	c.asym = arithsymbol.Is
   145  	return c
   146  }
   147  
   148  // IsNotNull set is not null
   149  func (c Criteria) IsNotNull() Criteria {
   150  	c.asym = arithsymbol.Not
   151  	return c
   152  }
   153  
   154  // In set in operator and column value, val should be a slice type value
   155  func (c Criteria) In(val interface{}) Criteria {
   156  	c.val = val
   157  	c.asym = arithsymbol.In
   158  	return c
   159  }
   160  
   161  // NotIn set not in operator and column value, val should be a slice type value
   162  func (c Criteria) NotIn(val interface{}) Criteria {
   163  	c.val = val
   164  	c.asym = arithsymbol.NotIn
   165  	return c
   166  }
   167  
   168  // Like set like operator and column value, val should be a slice type value
   169  func (c Criteria) Like(val interface{}) Criteria {
   170  	c.val = val
   171  	c.asym = arithsymbol.Like
   172  	return c
   173  }
   174  
   175  // And concat another sql expression builder with And
   176  func (c Criteria) And(cri Base) Where {
   177  	w := Where{
   178  		children: make([]Base, 0),
   179  	}
   180  	w.children = append(w.children, c, cri)
   181  	w.lsym = logicsymbol.And
   182  	return w
   183  }
   184  
   185  // Or concat another sql expression builder with Or
   186  func (c Criteria) Or(cri Base) Where {
   187  	w := Where{
   188  		children: make([]Base, 0),
   189  	}
   190  	w.children = append(w.children, c, cri)
   191  	w.lsym = logicsymbol.Or
   192  	return w
   193  }
   194  
   195  // Append concat another sql expression builder with Append
   196  func (c Criteria) Append(cri Base) Where {
   197  	w := Where{
   198  		children: make([]Base, 0),
   199  	}
   200  	w.children = append(w.children, c, cri)
   201  	w.lsym = logicsymbol.Append
   202  	return w
   203  }
   204  
   205  // End does nothing for Criteria same as Append empty
   206  func (c Criteria) End(q Base) Where {
   207  	w := Where{
   208  		children: make([]Base, 0),
   209  	}
   210  	w.children = append(w.children, c, q)
   211  	w.lsym = logicsymbol.End
   212  	return w
   213  }
   214  
   215  // Where concat children clauses with one of logic operators And, Or, Append
   216  type Where struct {
   217  	lsym     logicsymbol.LogicSymbol
   218  	children []Base
   219  }
   220  
   221  // Sql implement Base interface, return string sql expression
   222  func (w Where) Sql() (string, []interface{}) {
   223  	if len(w.children) == 0 {
   224  		return "", nil
   225  	}
   226  	var args []interface{}
   227  	w0, args0 := w.children[0].Sql()
   228  	args = append(args, args0...)
   229  	w1, args1 := w.children[1].Sql()
   230  	args = append(args, args1...)
   231  	switch w.lsym {
   232  	case logicsymbol.And, logicsymbol.Or:
   233  		if stringutils.IsNotEmpty(w0) && stringutils.IsNotEmpty(w1) {
   234  			return fmt.Sprintf("(%s %s %s)", w0, w.lsym, w1), args
   235  		} else {
   236  			if stringutils.IsNotEmpty(w0) {
   237  				return w0, args
   238  			} else if stringutils.IsNotEmpty(w1) {
   239  				return w1, args
   240  			} else {
   241  				return "", nil
   242  			}
   243  		}
   244  	case logicsymbol.Append:
   245  		if stringutils.IsNotEmpty(w0) && stringutils.IsNotEmpty(w1) {
   246  			return fmt.Sprintf("(%s%s%s)", w0, w.lsym, w1), args
   247  		} else {
   248  			if stringutils.IsNotEmpty(w0) {
   249  				return w0, args
   250  			} else if stringutils.IsNotEmpty(w1) {
   251  				return w1, args
   252  			} else {
   253  				return "", nil
   254  			}
   255  		}
   256  	case logicsymbol.End:
   257  		fallthrough
   258  	default:
   259  		if stringutils.IsEmpty(w1) {
   260  			return w0, args
   261  		}
   262  		return fmt.Sprintf("%s %s", w0, w1), args
   263  	}
   264  }
   265  
   266  // And concat another sql expression builder with And
   267  func (w Where) And(whe Base) Where {
   268  	parentW := Where{
   269  		children: make([]Base, 0),
   270  	}
   271  	parentW.children = append(parentW.children, w, whe)
   272  	parentW.lsym = logicsymbol.And
   273  	return parentW
   274  }
   275  
   276  // Or concat another sql expression builder with Or
   277  func (w Where) Or(whe Base) Where {
   278  	parentW := Where{
   279  		children: make([]Base, 0),
   280  	}
   281  	parentW.children = append(parentW.children, w, whe)
   282  	parentW.lsym = logicsymbol.Or
   283  	return parentW
   284  }
   285  
   286  // Append concat another sql expression builder with Append
   287  func (w Where) Append(whe Base) Where {
   288  	parentW := Where{
   289  		children: make([]Base, 0),
   290  	}
   291  	parentW.children = append(parentW.children, w, whe)
   292  	if _, ok := whe.(Page); ok {
   293  		parentW.lsym = logicsymbol.End
   294  	} else {
   295  		parentW.lsym = logicsymbol.Append
   296  	}
   297  	return parentW
   298  }
   299  
   300  func (w Where) End(q Base) Where {
   301  	parentW := Where{
   302  		children: make([]Base, 0),
   303  	}
   304  	parentW.children = append(parentW.children, w, q)
   305  	parentW.lsym = logicsymbol.End
   306  	return parentW
   307  }
   308  
   309  // Order by Col Sort
   310  type Order struct {
   311  	Col  string
   312  	Sort sortenum.Sort
   313  }
   314  
   315  // Page a sql expression builder for order by clause
   316  type Page struct {
   317  	Orders []Order
   318  	Offset int
   319  	Size   int
   320  }
   321  
   322  // P new a Page
   323  func P() Page {
   324  	return Page{
   325  		Orders: make([]Order, 0),
   326  	}
   327  }
   328  
   329  func NewPage(pageNo, pageSize int, orders ...Order) Page {
   330  	if pageNo <= 0 {
   331  		pageNo = 1
   332  	}
   333  	offset := 0
   334  	if pageSize > 0 {
   335  		offset = (pageNo - 1) * pageSize
   336  	}
   337  	return Page{
   338  		Offset: offset,
   339  		Size:   pageSize,
   340  		Orders: orders,
   341  	}
   342  }
   343  
   344  // Order append an Order
   345  func (p Page) Order(o Order) Page {
   346  	p.Orders = append(p.Orders, o)
   347  	return p
   348  }
   349  
   350  // Limit set Offset and Size
   351  func (p Page) Limit(offset, size int) Page {
   352  	p.Offset = offset
   353  	p.Size = size
   354  	return p
   355  }
   356  
   357  // Sql implement Base interface, order by age desc limit 2,1
   358  func (p Page) Sql() (string, []interface{}) {
   359  	var sb strings.Builder
   360  	var args []interface{}
   361  	if len(p.Orders) > 0 {
   362  		sb.WriteString("order by ")
   363  
   364  		for i, order := range p.Orders {
   365  			if i > 0 {
   366  				sb.WriteString(",")
   367  			}
   368  			var (
   369  				alias string
   370  				col   string
   371  			)
   372  			if strings.Contains(order.Col, ".") {
   373  				idx := strings.Index(order.Col, ".")
   374  				alias = order.Col[:idx]
   375  				col = order.Col[idx+1:]
   376  			} else {
   377  				col = order.Col
   378  			}
   379  			if stringutils.IsNotEmpty(alias) {
   380  				sb.WriteString(fmt.Sprintf("%s.`%s` %s", alias, col, order.Sort))
   381  			} else {
   382  				sb.WriteString(fmt.Sprintf("`%s` %s", col, order.Sort))
   383  			}
   384  		}
   385  	}
   386  
   387  	sb.WriteString(" ")
   388  
   389  	if p.Size > 0 {
   390  		sb.WriteString("limit ?,?")
   391  		args = append(args, p.Offset, p.Size)
   392  	}
   393  
   394  	return strings.TrimSpace(sb.String()), args
   395  }
   396  
   397  // PageRet wrap page query result
   398  type PageRet struct {
   399  	Items    interface{}
   400  	PageNo   int
   401  	PageSize int
   402  	Total    int
   403  	HasNext  bool
   404  }
   405  
   406  // NewPageRet new a PageRet
   407  func NewPageRet(page Page) PageRet {
   408  	pageNo := 1
   409  	if page.Size > 0 {
   410  		pageNo = page.Offset/page.Size + 1
   411  	}
   412  	return PageRet{
   413  		PageNo:   pageNo,
   414  		PageSize: page.Size,
   415  	}
   416  }
   417  
   418  // String is an alias of string
   419  type String string
   420  
   421  // Sql implements Base
   422  func (s String) Sql() (string, []interface{}) {
   423  	return string(s), nil
   424  }