-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathquery.go
151 lines (120 loc) · 4.08 KB
/
query.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
package gormlike
import (
"fmt"
"strings"
"gorm.io/gorm"
"gorm.io/gorm/clause"
)
const tagName = "gormlike"
func (d *gormLike) replaceExpressions(db *gorm.DB, expressions []clause.Expression) []clause.Expression {
for index, cond := range expressions {
switch cond := cond.(type) {
case clause.AndConditions:
// Recursively go through the expressions of AndConditions
cond.Exprs = d.replaceExpressions(db, cond.Exprs)
expressions[index] = cond
case clause.OrConditions:
// Recursively go through the expressions of OrConditions
cond.Exprs = d.replaceExpressions(db, cond.Exprs)
expressions[index] = cond
case clause.Eq:
columnName, columnOk := cond.Column.(string)
if !columnOk {
continue
}
// Get the `gormlike` value
var tagValue string
dbField, ok := db.Statement.Schema.FieldsByDBName[columnName]
if ok {
tagValue = dbField.Tag.Get(tagName)
}
// If the user has explicitly set this to false, ignore this field
if tagValue == "false" {
continue
}
// If tags are required and the tag is not true, ignore this field
if d.conditionalTag && tagValue != "true" {
continue
}
value, columnOk := cond.Value.(string)
if !columnOk {
continue
}
// If there are no % AND there aren't ony replaceable characters, just skip it because it's a normal query
if !strings.Contains(value, "%") && !(d.replaceCharacter != "" && strings.Contains(value, d.replaceCharacter)) {
continue
}
condition := fmt.Sprintf("CAST(%s as varchar) LIKE ?", cond.Column)
if d.replaceCharacter != "" {
value = strings.ReplaceAll(value, d.replaceCharacter, "%")
}
expressions[index] = db.Session(&gorm.Session{NewDB: true}).Where(condition, value).Statement.Clauses["WHERE"].Expression
case clause.IN:
columnName, columnOk := cond.Column.(string)
if !columnOk {
continue
}
// Get the `gormlike` value
var tagValue string
dbField, ok := db.Statement.Schema.FieldsByDBName[columnName]
if ok {
tagValue = dbField.Tag.Get(tagName)
}
// If the user has explicitly set this to false, ignore this field
if tagValue == "false" {
continue
}
// If tags are required and the tag is not true, ignore this field
if d.conditionalTag && tagValue != "true" {
continue
}
var likeCounter int
query := db.Session(&gorm.Session{NewDB: true})
for _, value := range cond.Values {
value, ok := value.(string)
if !ok {
continue
}
condition := fmt.Sprintf("%s = ?", cond.Column)
// If there are no % AND there aren't ony replaceable characters, just skip it because it's a normal query
if strings.Contains(value, "%") || (d.replaceCharacter != "" && strings.Contains(value, d.replaceCharacter)) {
condition = fmt.Sprintf("CAST(%s as varchar) LIKE ?", cond.Column)
if d.replaceCharacter != "" {
value = strings.ReplaceAll(value, d.replaceCharacter, "%")
}
likeCounter++
}
query = query.Or(condition, value)
}
// Don't alter the query if it isn't necessary
if likeCounter == 0 {
continue
}
// This feels a bit like a dirty hack
// but otherwise the generated query would not be correct in case of an AND condition between multiple OR conditions
// e.g. without this -> x = .. OR x = .. AND y = .. OR y = .. (no brackets around the OR conditions mess up the query)
// e.g. with this -> (x = .. OR x = ..) AND (y = .. OR y = ..)
var newExpression clause.OrConditions
newExpression.Exprs = query.Statement.Clauses["WHERE"].Expression.(clause.Where).Exprs
expressions[index] = newExpression
}
}
return expressions
}
func (d *gormLike) queryCallback(db *gorm.DB) {
// If we only want to like queries that are explicitly set to true, we back out early if anything's amiss
settingValue, settingOk := db.Get(tagName)
if d.conditionalSetting && !settingOk {
return
}
if settingOk {
if boolValue, _ := settingValue.(bool); !boolValue {
return
}
}
exp, settingOk := db.Statement.Clauses["WHERE"].Expression.(clause.Where)
if !settingOk {
return
}
exp.Exprs = d.replaceExpressions(db, exp.Exprs)
}