github.com/eris-ltd/erisdb@v0.25.0/event/query/builder.go (about)

     1  package query
     2  
     3  import (
     4  	"bytes"
     5  	"encoding"
     6  	"fmt"
     7  	"reflect"
     8  	"strconv"
     9  	"strings"
    10  	"text/template"
    11  	"time"
    12  )
    13  
    14  const (
    15  	MultipleValueTagSeparator = ";"
    16  
    17  	// Operators
    18  	equalString          = "="
    19  	greaterThanString    = ">"
    20  	lessThanString       = "<"
    21  	greaterOrEqualString = ">="
    22  	lessOrEqualString    = "<="
    23  	containsString       = "CONTAINS"
    24  	andString            = "AND"
    25  
    26  	// Values
    27  	trueString  = "true"
    28  	falseString = "false"
    29  	emptyString = "empty"
    30  	timeString  = "TIME"
    31  	dateString  = "DATE"
    32  )
    33  
    34  type Query interface {
    35  	Matches(tags Tagged) bool
    36  	String() string
    37  }
    38  
    39  type Queryable interface {
    40  	Query() (Query, error)
    41  }
    42  
    43  type parsedQuery struct {
    44  	query Query
    45  }
    46  
    47  func AsQueryable(query Query) parsedQuery {
    48  	return parsedQuery{query: query}
    49  }
    50  
    51  func (pq parsedQuery) Query() (Query, error) {
    52  	return pq.query, nil
    53  }
    54  
    55  // A yet-to-parsed query
    56  type String string
    57  
    58  func Must(qry Query, err error) Query {
    59  	if err != nil {
    60  		panic(fmt.Errorf("could not compile: %v", qry))
    61  	}
    62  	return qry
    63  }
    64  
    65  func (qs String) Query() (Query, error) {
    66  	if isEmpty(string(qs)) {
    67  		return Empty{}, nil
    68  	}
    69  	return New(string(qs))
    70  }
    71  
    72  func MatchAllQueryable() Queryable {
    73  	return Empty{}
    74  }
    75  
    76  // A fluent query builder
    77  type Builder struct {
    78  	queryString string
    79  	condition
    80  	// reusable buffer for building queryString
    81  	bytes.Buffer
    82  	error
    83  }
    84  
    85  // Templates
    86  type condition struct {
    87  	Tag     string
    88  	Op      string
    89  	Operand string
    90  }
    91  
    92  var conditionTemplate = template.Must(template.New("condition").Parse("{{.Tag}} {{.Op}} {{.Operand}}"))
    93  
    94  // Creates a new query builder with a base query that is the conjunction of all queries passed
    95  func NewBuilder(queries ...string) *Builder {
    96  	qb := new(Builder)
    97  	qb.queryString = qb.and(stringIterator(queries...))
    98  	return qb
    99  }
   100  
   101  func (qb *Builder) String() string {
   102  	return qb.queryString
   103  }
   104  
   105  func (qb *Builder) Query() (Query, error) {
   106  	if qb.error != nil {
   107  		return nil, qb.error
   108  	}
   109  	return NewOrEmpty(qb.queryString)
   110  }
   111  
   112  func NewOrEmpty(queryString string) (Query, error) {
   113  	if isEmpty(queryString) {
   114  		return Empty{}, nil
   115  	}
   116  	return New(queryString)
   117  }
   118  
   119  // Creates the conjunction of Builder and rightQuery
   120  func (qb *Builder) And(queryBuilders ...*Builder) *Builder {
   121  	return NewBuilder(qb.and(queryBuilderIterator(queryBuilders...)))
   122  }
   123  
   124  // Creates the conjunction of Builder and tag = operand
   125  func (qb *Builder) AndEquals(tag string, operand interface{}) *Builder {
   126  	qb.condition.Tag = tag
   127  	qb.condition.Op = equalString
   128  	qb.condition.Operand = operandString(operand)
   129  	return NewBuilder(qb.and(stringIterator(qb.conditionString())))
   130  }
   131  
   132  func (qb *Builder) AndGreaterThanOrEqual(tag string, operand interface{}) *Builder {
   133  	qb.condition.Tag = tag
   134  	qb.condition.Op = greaterOrEqualString
   135  	qb.condition.Operand = operandString(operand)
   136  	return NewBuilder(qb.and(stringIterator(qb.conditionString())))
   137  }
   138  
   139  func (qb *Builder) AndLessThanOrEqual(tag string, operand interface{}) *Builder {
   140  	qb.condition.Tag = tag
   141  	qb.condition.Op = lessOrEqualString
   142  	qb.condition.Operand = operandString(operand)
   143  	return NewBuilder(qb.and(stringIterator(qb.conditionString())))
   144  }
   145  
   146  func (qb *Builder) AndStrictlyGreaterThan(tag string, operand interface{}) *Builder {
   147  	qb.condition.Tag = tag
   148  	qb.condition.Op = greaterThanString
   149  	qb.condition.Operand = operandString(operand)
   150  	return NewBuilder(qb.and(stringIterator(qb.conditionString())))
   151  }
   152  
   153  func (qb *Builder) AndStrictlyLessThan(tag string, operand interface{}) *Builder {
   154  	qb.condition.Tag = tag
   155  	qb.condition.Op = lessThanString
   156  	qb.condition.Operand = operandString(operand)
   157  	return NewBuilder(qb.and(stringIterator(qb.conditionString())))
   158  }
   159  
   160  func (qb *Builder) AndContains(tag string, operand interface{}) *Builder {
   161  	qb.condition.Tag = tag
   162  	qb.condition.Op = containsString
   163  	qb.condition.Operand = operandString(operand)
   164  	return NewBuilder(qb.and(stringIterator(qb.conditionString())))
   165  }
   166  
   167  func (qb *Builder) and(queryIterator func(func(string))) string {
   168  	defer qb.Buffer.Reset()
   169  	qb.Buffer.WriteString(qb.queryString)
   170  	queryIterator(func(q string) {
   171  		if !isEmpty(q) {
   172  			if qb.Buffer.Len() > 0 {
   173  				qb.Buffer.WriteByte(' ')
   174  				qb.Buffer.WriteString(andString)
   175  				qb.Buffer.WriteByte(' ')
   176  			}
   177  			qb.Buffer.WriteString(q)
   178  		}
   179  	})
   180  	return qb.Buffer.String()
   181  }
   182  
   183  func operandString(value interface{}) string {
   184  	buf := new(bytes.Buffer)
   185  	switch v := value.(type) {
   186  	case string:
   187  		buf.WriteByte('\'')
   188  		buf.WriteString(v)
   189  		buf.WriteByte('\'')
   190  		return buf.String()
   191  	case fmt.Stringer:
   192  		return operandString(v.String())
   193  	default:
   194  		return StringFromValue(v)
   195  	}
   196  }
   197  
   198  func StringFromValue(value interface{}) string {
   199  	rv := reflect.ValueOf(value)
   200  	if rv.Kind() == reflect.Ptr && rv.IsNil() {
   201  		return "nil"
   202  	}
   203  	switch v := value.(type) {
   204  	case string:
   205  		return v
   206  	case encoding.TextMarshaler:
   207  		bs, _ := v.MarshalText()
   208  		return string(bs)
   209  	case fmt.Stringer:
   210  		return v.String()
   211  	case bool:
   212  		if v {
   213  			return trueString
   214  		}
   215  		return falseString
   216  	case int:
   217  		return strconv.FormatInt(int64(v), 10)
   218  	case int32:
   219  		return strconv.FormatInt(int64(v), 10)
   220  	case int64:
   221  		return strconv.FormatInt(v, 10)
   222  	case uint:
   223  		return strconv.FormatUint(uint64(v), 10)
   224  	case uint32:
   225  		return strconv.FormatUint(uint64(v), 10)
   226  	case uint64:
   227  		return strconv.FormatUint(v, 10)
   228  	case float32:
   229  		return strconv.FormatFloat(float64(v), 'f', -1, 32)
   230  	case float64:
   231  		return strconv.FormatFloat(float64(v), 'f', -1, 64)
   232  	case time.Time:
   233  		return timeString + " " + v.Format(time.RFC3339)
   234  	default:
   235  		if rv.Kind() == reflect.Slice {
   236  			values := make([]string, rv.Len())
   237  			for i := 0; i < rv.Len(); i++ {
   238  				values[i] = StringFromValue(rv.Index(i).Interface())
   239  			}
   240  			return strings.Join(values, MultipleValueTagSeparator)
   241  		}
   242  		return fmt.Sprintf("%v", v)
   243  	}
   244  }
   245  
   246  func (qb *Builder) conditionString() string {
   247  	defer qb.Buffer.Reset()
   248  	err := conditionTemplate.Execute(&qb.Buffer, qb.condition)
   249  	if err != nil && qb.error == nil {
   250  		qb.error = err
   251  	}
   252  	return qb.Buffer.String()
   253  }
   254  
   255  func isEmpty(queryString string) bool {
   256  	return queryString == "" || queryString == emptyString
   257  }
   258  
   259  // Iterators over some strings
   260  func stringIterator(strs ...string) func(func(string)) {
   261  	return func(callback func(string)) {
   262  		for _, s := range strs {
   263  			callback(s)
   264  		}
   265  	}
   266  }
   267  
   268  func queryBuilderIterator(qbs ...*Builder) func(func(string)) {
   269  	return func(callback func(string)) {
   270  		for _, qb := range qbs {
   271  			callback(qb.String())
   272  		}
   273  	}
   274  }