-
Notifications
You must be signed in to change notification settings - Fork 0
/
query.go
131 lines (110 loc) · 3.4 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
package oven
import (
"context"
"cloud.google.com/go/firestore"
"github.com/pkg/errors"
"google.golang.org/api/iterator"
)
const (
// OperationTypeEqual is equal to.
OperationTypeEqual OperationType = "=="
// OperationTypeNotEqual is not equal to.
OperationTypeNotEqual OperationType = "!="
// OperationTypeLessThan is less than.
OperationTypeLessThan OperationType = "<"
// OperationTypeLessThanOrEqual is less than or equal to.
OperationTypeLessThanOrEqual OperationType = "<="
// OperationTypeGreaterThan is greater than.
OperationTypeGreaterThan OperationType = ">"
// OperationTypeGreaterThanOrEqual is greater than or equal to.
OperationTypeGreaterThanOrEqual OperationType = ">="
// OperationTypeArrayContains is array contains.
OperationTypeArrayContains OperationType = "array-contains"
// OperationTypeArrayContainsAny is array contains any.
OperationTypeArrayContainsAny OperationType = "array-contains-any"
// OperationTypeIn is in.
OperationTypeIn OperationType = "in"
// OperationTypeNotIn is not in.
OperationTypeNotIn OperationType = "not-in"
)
var (
// ErrInvalidDestinationType is thrown when the item type is not a pointer to a struct.
ErrInvalidDestinationType = errors.New("destination type must be a non nil pointer")
)
// Criteria represents a query to be executed against the Firestore.
type Criteria struct {
Collection string
Criterions []*Criterion
OrderBy string
Desc bool
Limit int
}
// Criterion represents a single criteria for a query.
type Criterion struct {
Path string
Operation OperationType
Value interface{}
}
// OperationType represents the type of operation to be performed.
type OperationType string
// Query retreaves access info for all users since last update.
func Query[T any](ctx context.Context, client *firestore.Client, c *Criteria) ([]*T, error) {
if c == nil || c.Collection == "" {
return nil, errors.Errorf("valid query required: %+v", c)
}
// collection
col, err := GetCollection(client, c.Collection)
if err != nil {
return nil, errors.Wrapf(err, "error getting collection %s", c.Collection)
}
// where
for _, r := range c.Criterions {
col.Where(r.Path, string(r.Operation), r.Value)
}
// desc?
dir := firestore.Desc
if !c.Desc {
dir = firestore.Asc
}
// iterate
it := col.OrderBy(c.OrderBy, dir).Limit(c.Limit).Documents(ctx)
return ToStructs[T](it)
}
// ToStructs converts the Firestore document iterator into a slice of structs.
func ToStructs[T any](it *firestore.DocumentIterator) ([]*T, error) {
return ToStructsWithHandler[T](it, nil)
}
// ToStructsWithHandler converts the Firestore document iterator into a slice of structs.
func ToStructsWithHandler[T any](it *firestore.DocumentIterator, h func(item *T) (bool, error)) ([]*T, error) {
if it == nil {
return nil, errors.Errorf("valid iterator required")
}
list := make([]*T, 0)
for {
d, e := it.Next()
if e == iterator.Done {
break
}
if e != nil {
return nil, e
}
var t T
if e = d.DataTo(&t); e != nil {
return nil, errors.Errorf("error converting data to struct: %v", e)
}
// if no handler is provided, just add the struct to the list
if h == nil {
list = append(list, &t)
continue
}
// if the handler is provided, only add when it returns true
ok, e := h(&t)
if e != nil {
return nil, errors.Wrap(e, "error executing iterator handler")
}
if ok {
list = append(list, &t)
}
}
return list, nil
}