This repository has been archived by the owner on Dec 29, 2018. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1
/
conditions.go
161 lines (139 loc) · 4.57 KB
/
conditions.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
package database
import (
"fmt"
"reflect"
"strings"
)
// Condition should be implemented by any generic SQL condition we can apply to collections.
type Condition interface {
// SQL returns the portion of the code that will be merged inside the WHERE
// query. It can have placeholders with "?" to fill them apart.
SQL() string
// Values returns the list of placeholders values we should fill.
Values() []interface{}
}
// Filter applies a new simple filter to the collection. There are multiple types
// of simple filters depending on the SQL you pass to it:
//
// Filter("foo", "bar")
// Filter("foo >", 3)
// Filter("foo LIKE", "%bar%")
// Filter("DATE_DIFF(?, mycolumn) > 30", time.Now())
func Filter(sql string, value interface{}) Condition {
var queryValues []interface{}
if !strings.Contains(sql, " ") {
sql = fmt.Sprintf("%s = ?", sql)
queryValues = []interface{}{value}
} else if strings.Contains(sql, " IN") {
v := reflect.ValueOf(value)
placeholders := make([]string, v.Len())
for i := 0; i < v.Len(); i++ {
placeholders[i] = "?"
queryValues = append(queryValues, v.Index(i).Interface())
}
sql = fmt.Sprintf("%s (%s)", sql, strings.Join(placeholders, ", "))
} else if !strings.Contains(sql, "?") {
sql = fmt.Sprintf("%s ?", sql)
queryValues = []interface{}{value}
} else {
queryValues = []interface{}{value}
}
return &sqlCondition{sql, queryValues}
}
// CompareJSON creates a new condition that checks if a value inside a JSON
// object of a column is equal to the provided value.
func CompareJSON(column, path string, value interface{}) Condition {
return &sqlCondition{
sql: fmt.Sprintf("JSON_EXTRACT(%s, '%s') = ?", column, path),
values: []interface{}{value},
}
}
// FilterExists checks if a subquery matches for each row before accepting it. It will use
// the join SQL statement as an additional filter to those ones both queries have to join the
// rows of two queries. Not having a join statement will throw a panic.
//
// No external parameters are allowed in the join statement because they can be supplied through
// normal filters in both collections. Limit yourself to relate both tables to make the FilterExists
// call useful.
//
// You can alias both collections to use shorter names in the statement. It is recommend to
// always use aliases when referring to the columns in the join statement.
func FilterExists(sub *Collection, join string) Condition {
if join == "" {
panic("join SQL statement is required to FilterExists")
}
sub = sub.Clone().FilterCond(&sqlCondition{join, nil})
b := &sqlBuilder{
table: sub.model.TableName(),
conditions: sub.conditions,
props: sub.props,
limit: sub.limit,
offset: sub.offset,
orders: sub.orders,
alias: sub.alias,
}
sql, values := b.SelectSQLCols("NULL")
return &sqlCondition{fmt.Sprintf("EXISTS (%s)", sql), values}
}
type sqlCondition struct {
sql string
values []interface{}
}
func (cond *sqlCondition) SQL() string {
return cond.sql
}
func (cond *sqlCondition) Values() []interface{} {
return cond.values
}
// And applies an AND operation between each of the children conditions.
func And(children []Condition) Condition {
if len(children) == 0 {
return new(sqlCondition)
}
sql := make([]string, len(children))
values := []interface{}{}
for i, child := range children {
sql[i] = child.SQL()
values = append(values, child.Values()...)
}
return &sqlCondition{
sql: "(" + strings.Join(sql, " AND ") + ")",
values: values,
}
}
// Or applies an OR operation between each of the children conditions.
func Or(children []Condition) Condition {
if len(children) == 0 {
return new(sqlCondition)
}
sql := make([]string, len(children))
values := []interface{}{}
for i, child := range children {
sql[i] = child.SQL()
values = append(values, child.Values()...)
}
return &sqlCondition{
sql: "(" + strings.Join(sql, " OR ") + ")",
values: values,
}
}
// EscapeLike escapes a value to insert it in a LIKE query without unexpected wildcards.
// After using this function to clean the value you can add the wildcards you need
// to the query.
func EscapeLike(str string) string {
str = strings.Replace(str, "%", `\%`, -1)
str = strings.Replace(str, "_", `\_`, -1)
return str
}
// FilterIsNil filter rows with with NULL in the column.
func FilterIsNil(column string) Condition {
return &sqlCondition{
sql: fmt.Sprintf("%s IS NULL", column),
}
}
// FilterIsNotNil filter rows with with something other than NULL in the column.
func FilterIsNotNil(column string) Condition {
return &sqlCondition{
sql: fmt.Sprintf("%s IS NOT NULL", column),
}
}