Skip to content

Commit 99f04ad

Browse files
committed
Refactor query builder for mongo and firestore
1 parent ac480d6 commit 99f04ad

2 files changed

Lines changed: 240 additions & 215 deletions

File tree

firestore/query/query_builder.go

Lines changed: 137 additions & 109 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,44 @@
11
package query
22

33
import (
4-
"log"
54
"reflect"
65
"strings"
76

87
"github.com/core-go/search"
98
f "github.com/core-go/search/firestore"
109
)
1110

12-
type Builder[T any, F any] struct {
11+
type Builder[F any] struct {
1312
ModelType reflect.Type
1413
}
1514

16-
func UseQuery[T any, F any]() func(F) ([]f.Query, []string) {
17-
bu := NewBuilder[T, F]()
18-
return bu.BuildQuery
15+
func NewBuilder[F any](resultModelType reflect.Type) *Builder[F] {
16+
return &Builder[F]{ModelType: resultModelType}
1917
}
20-
21-
func NewBuilder[T any, F any]() *Builder[T, F] {
18+
func UseQuery[T any, F any]() func(F) ([]f.Query, []string) {
2219
var t T
2320
resultModelType := reflect.TypeOf(t)
24-
if resultModelType.Kind() == reflect.Ptr {
25-
resultModelType = resultModelType.Elem()
26-
}
27-
return &Builder[T, F]{ModelType: resultModelType}
21+
b := NewBuilder[F](resultModelType)
22+
return b.BuildQuery
2823
}
29-
func (b *Builder[T, F]) BuildQuery(filter F) ([]f.Query, []string) {
24+
func (b *Builder[F]) BuildQuery(filter F) ([]f.Query, []string) {
3025
return BuildQueryByType(filter, b.ModelType)
3126
}
3227

28+
var operators = map[string]string{
29+
"=": "==",
30+
"==": "==",
31+
"!=": "!=",
32+
">": ">",
33+
">=": ">=",
34+
"<": "<",
35+
"<=": "<=",
36+
"array-contains": "array-contains",
37+
"array-contains-any": "array-contains-any",
38+
"in": "in",
39+
"not-in": "not-in",
40+
}
41+
3342
func BuildQueryByType(filter interface{}, resultModelType reflect.Type) ([]f.Query, []string) {
3443
var query = make([]f.Query, 0)
3544
fields := make([]string, 0)
@@ -39,34 +48,34 @@ func BuildQueryByType(filter interface{}, resultModelType reflect.Type) ([]f.Que
3948
}
4049

4150
value := reflect.Indirect(reflect.ValueOf(filter))
51+
filterType := value.Type()
4252
numField := value.NumField()
43-
var keyword string
44-
keywordFormat := map[string]string{
45-
"prefix": "==",
46-
"contain": "==",
47-
"equal": "==",
48-
}
4953
for i := 0; i < numField; i++ {
54+
fsName := getFirestore(filterType, i)
55+
if fsName == "-" {
56+
continue
57+
}
58+
filterType := value.Type()
59+
operator := "=="
60+
if key, ok := filterType.Field(i).Tag.Lookup("operator"); ok && len(key) > 0 {
61+
oper, ok2 := operators[key]
62+
if ok2 {
63+
operator = oper
64+
}
65+
}
66+
5067
field := value.Field(i)
5168
kind := field.Kind()
5269
x := field.Interface()
53-
ps := false
5470
var psv string
5571
if kind == reflect.Ptr {
5672
if field.IsNil() {
5773
continue
74+
} else {
75+
field = field.Elem()
76+
kind = field.Kind()
77+
x = field.Interface()
5878
}
59-
s0, ok0 := x.(*string)
60-
if ok0 {
61-
if s0 == nil || len(*s0) == 0 {
62-
continue
63-
}
64-
ps = true
65-
psv = *s0
66-
}
67-
field = field.Elem()
68-
x = field.Interface()
69-
kind = field.Kind()
7079
}
7180
s0, ok0 := x.(string)
7281
if ok0 {
@@ -75,119 +84,131 @@ func BuildQueryByType(filter interface{}, resultModelType reflect.Type) ([]f.Que
7584
}
7685
psv = s0
7786
}
78-
ks := kind.String()
87+
if len(fsName) == 0 {
88+
fsName = getFirestoreName(resultModelType, filterType.Field(i).Name)
89+
}
7990
if v, ok := x.(search.Filter); ok {
8091
if len(v.Fields) > 0 {
8192
for _, key := range v.Fields {
82-
i, _, columnName := getFieldByJson(resultModelType, key)
83-
if len(columnName) <= 0 {
93+
i, _, fsName := getFieldByJson(resultModelType, key)
94+
if len(fsName) <= 0 {
8495
fields = fields[len(fields):]
8596
break
8697
} else if i == -1 {
87-
columnName = key
88-
}
89-
fields = append(fields, columnName)
90-
}
91-
}
92-
if len(v.Q) > 0 {
93-
keyword = strings.TrimSpace(v.Q)
94-
}
95-
continue
96-
} else if ps || ks == "string" {
97-
var keywordQuery f.Query
98-
columnName := getFirestoreName(resultModelType, value.Type().Field(i).Name)
99-
// var operator string
100-
operator := "=="
101-
var searchValue interface{}
102-
if len(psv) > 0 {
103-
const defaultKey = "contain"
104-
if key, ok := value.Type().Field(i).Tag.Lookup("operator"); ok && len(key) > 0 {
105-
operator = key
106-
} else {
107-
if key, ok := value.Type().Field(i).Tag.Lookup("match"); ok {
108-
if format, exist := keywordFormat[key]; exist {
109-
operator = format
110-
} else {
111-
log.Panicf("match not support \"%v\" format\n", key)
112-
}
113-
} else if format, exist := keywordFormat[defaultKey]; exist {
114-
operator = format
98+
fsName = key
11599
}
116-
}
117-
searchValue = psv
118-
} else if len(keyword) > 0 {
119-
if key, ok := value.Type().Field(i).Tag.Lookup("keyword"); ok {
120-
if format, exist := keywordFormat[key]; exist {
121-
operator = format
122-
} else {
123-
log.Panicf("keyword not support \"%v\" format\n", key)
100+
if len(fsName) > 0 && fsName != "-" {
101+
fields = append(fields, fsName)
124102
}
125103
}
126-
searchValue = keyword
127-
}
128-
if len(columnName) > 0 && len(operator) > 0 {
129-
keywordQuery = f.Query{Path: columnName, Operator: operator, Value: searchValue}
130-
query = append(query, keywordQuery)
131104
}
105+
continue
106+
} else if len(fsName) == 0 {
107+
continue
108+
}
109+
if len(psv) > 0 {
110+
query = append(query, f.Query{Path: fsName, Operator: operator, Value: psv})
132111
} else if rangeTime, ok := x.(search.TimeRange); ok {
133-
columnName := getFirestoreName(resultModelType, value.Type().Field(i).Name)
134-
actionTimeQuery := make([]f.Query, 0)
112+
timeQuery := make([]f.Query, 0)
135113
if rangeTime.Min == nil {
136-
actionTimeQuery = []f.Query{{Path: columnName, Operator: "<=", Value: rangeTime.Max}}
114+
timeQuery = []f.Query{{Path: fsName, Operator: "<=", Value: rangeTime.Max}}
137115
} else if rangeTime.Max == nil {
138-
actionTimeQuery = []f.Query{{Path: columnName, Operator: ">=", Value: rangeTime.Min}}
116+
timeQuery = []f.Query{{Path: fsName, Operator: ">=", Value: rangeTime.Min}}
139117
} else {
140-
actionTimeQuery = []f.Query{{Path: columnName, Operator: "<=", Value: rangeTime.Max}, {Path: columnName, Operator: ">=", Value: rangeTime.Min}}
118+
timeQuery = []f.Query{{Path: fsName, Operator: ">=", Value: rangeTime.Min}, {Path: fsName, Operator: "<=", Value: rangeTime.Max}}
141119
}
142-
query = append(query, actionTimeQuery...)
120+
query = append(query, timeQuery...)
143121
} else if rangeDate, ok := x.(search.DateRange); ok {
144-
columnName := getFirestoreName(resultModelType, value.Type().Field(i).Name)
145-
actionDateQuery := make([]f.Query, 0)
122+
dateQuery := make([]f.Query, 0)
146123
if rangeDate.Min == nil && rangeDate.Max == nil {
147124
continue
148125
} else if rangeDate.Min == nil {
149-
actionDateQuery = []f.Query{{Path: columnName, Operator: "<=", Value: rangeDate.Max}}
126+
dateQuery = []f.Query{{Path: fsName, Operator: "<=", Value: rangeDate.Max}}
150127
} else if rangeDate.Max == nil {
151-
actionDateQuery = []f.Query{{Path: columnName, Operator: ">=", Value: rangeDate.Min}}
128+
dateQuery = []f.Query{{Path: fsName, Operator: ">=", Value: rangeDate.Min}}
152129
} else {
153-
actionDateQuery = []f.Query{{Path: columnName, Operator: "<=", Value: rangeDate.Max}, {Path: columnName, Operator: ">=", Value: rangeDate.Min}}
130+
dateQuery = []f.Query{{Path: fsName, Operator: ">=", Value: rangeDate.Min}, {Path: fsName, Operator: "<=", Value: rangeDate.Max}}
154131
}
155-
query = append(query, actionDateQuery...)
132+
query = append(query, dateQuery...)
156133
} else if numberRange, ok := x.(search.NumberRange); ok {
157-
columnName := getFirestoreName(resultModelType, value.Type().Field(i).Name)
158-
amountQuery := make([]f.Query, 0)
134+
numQuery := make([]f.Query, 0)
159135

160136
if numberRange.Min != nil {
161-
amountQuery = append(amountQuery, f.Query{Path: columnName, Operator: ">=", Value: *numberRange.Min})
137+
numQuery = append(numQuery, f.Query{Path: fsName, Operator: ">=", Value: *numberRange.Min})
162138
} else if numberRange.Lower != nil {
163-
amountQuery = append(amountQuery, f.Query{Path: columnName, Operator: ">", Value: *numberRange.Lower})
139+
numQuery = append(numQuery, f.Query{Path: fsName, Operator: ">", Value: *numberRange.Lower})
164140
}
165141
if numberRange.Max != nil {
166-
amountQuery = append(amountQuery, f.Query{Path: columnName, Operator: "<=", Value: *numberRange.Max})
142+
numQuery = append(numQuery, f.Query{Path: fsName, Operator: "<=", Value: *numberRange.Max})
167143
} else if numberRange.Upper != nil {
168-
amountQuery = append(amountQuery, f.Query{Path: columnName, Operator: "<", Value: *numberRange.Upper})
144+
numQuery = append(numQuery, f.Query{Path: fsName, Operator: "<", Value: *numberRange.Upper})
169145
}
170146

171-
if len(amountQuery) > 0 {
172-
query = append(query, amountQuery...)
147+
if len(numQuery) > 0 {
148+
query = append(query, numQuery...)
173149
}
174-
} else if ks == "slice" && reflect.Indirect(reflect.ValueOf(x)).Len() > 0 {
175-
columnName := getFirestoreName(resultModelType, value.Type().Field(i).Name)
176-
q := f.Query{Path: columnName, Operator: "in", Value: x}
177-
query = append(query, q)
178-
} else {
179-
if _, ok := x.(search.Filter); ks == "bool" || (strings.Contains(ks, "int") && x != 0) || (strings.Contains(ks, "float") && x != 0) || (!ok && ks == "ptr" && field.Pointer() != 0) {
180-
v := value.Type().Field(i).Name
181-
columnName := getFirestoreName(resultModelType, v)
182-
if len(columnName) > 0 {
183-
oper := "=="
184-
if key, ok := value.Type().Field(i).Tag.Lookup("operator"); ok && len(key) > 0 {
185-
oper = key
186-
}
187-
q := f.Query{Path: columnName, Operator: oper, Value: x}
188-
query = append(query, q)
150+
} else if numberRange, ok := x.(search.Int64Range); ok {
151+
numQuery := make([]f.Query, 0)
152+
153+
if numberRange.Min != nil {
154+
numQuery = append(numQuery, f.Query{Path: fsName, Operator: ">=", Value: *numberRange.Min})
155+
} else if numberRange.Lower != nil {
156+
numQuery = append(numQuery, f.Query{Path: fsName, Operator: ">", Value: *numberRange.Lower})
157+
}
158+
if numberRange.Max != nil {
159+
numQuery = append(numQuery, f.Query{Path: fsName, Operator: "<=", Value: *numberRange.Max})
160+
} else if numberRange.Upper != nil {
161+
numQuery = append(numQuery, f.Query{Path: fsName, Operator: "<", Value: *numberRange.Upper})
162+
}
163+
164+
if len(numQuery) > 0 {
165+
query = append(query, numQuery...)
166+
}
167+
} else if numberRange, ok := x.(search.IntRange); ok {
168+
numQuery := make([]f.Query, 0)
169+
170+
if numberRange.Min != nil {
171+
numQuery = append(numQuery, f.Query{Path: fsName, Operator: ">=", Value: *numberRange.Min})
172+
} else if numberRange.Lower != nil {
173+
numQuery = append(numQuery, f.Query{Path: fsName, Operator: ">", Value: *numberRange.Lower})
174+
}
175+
if numberRange.Max != nil {
176+
numQuery = append(numQuery, f.Query{Path: fsName, Operator: "<=", Value: *numberRange.Max})
177+
} else if numberRange.Upper != nil {
178+
numQuery = append(numQuery, f.Query{Path: fsName, Operator: "<", Value: *numberRange.Upper})
179+
}
180+
181+
if len(numQuery) > 0 {
182+
query = append(query, numQuery...)
183+
}
184+
} else if numberRange, ok := x.(search.Int32Range); ok {
185+
numQuery := make([]f.Query, 0)
186+
187+
if numberRange.Min != nil {
188+
numQuery = append(numQuery, f.Query{Path: fsName, Operator: ">=", Value: *numberRange.Min})
189+
} else if numberRange.Lower != nil {
190+
numQuery = append(numQuery, f.Query{Path: fsName, Operator: ">", Value: *numberRange.Lower})
191+
}
192+
if numberRange.Max != nil {
193+
numQuery = append(numQuery, f.Query{Path: fsName, Operator: "<=", Value: *numberRange.Max})
194+
} else if numberRange.Upper != nil {
195+
numQuery = append(numQuery, f.Query{Path: fsName, Operator: "<", Value: *numberRange.Upper})
196+
}
197+
198+
if len(numQuery) > 0 {
199+
query = append(query, numQuery...)
200+
}
201+
} else if kind == reflect.Slice {
202+
if reflect.Indirect(reflect.ValueOf(x)).Len() > 0 {
203+
if operator == "==" {
204+
operator = "in"
189205
}
206+
q := f.Query{Path: fsName, Operator: operator, Value: x}
207+
query = append(query, q)
190208
}
209+
} else {
210+
q := f.Query{Path: fsName, Operator: operator, Value: x}
211+
query = append(query, q)
191212
}
192213
}
193214
return query, fields
@@ -216,3 +237,10 @@ func getFirestoreName(modelType reflect.Type, fieldName string) string {
216237
}
217238
return fieldName
218239
}
240+
func getFirestore(filterType reflect.Type, i int) string {
241+
field := filterType.Field(i)
242+
if tag, ok := field.Tag.Lookup("firestore"); ok {
243+
return strings.Split(tag, ",")[0]
244+
}
245+
return ""
246+
}

0 commit comments

Comments
 (0)