store - search.go
1 package store
2
3 import (
4 "database/sql"
5 "reflect"
6 )
7
8 type SortBy struct {
9 Column string
10 Asc bool
11 }
12
13 type Search struct {
14 store *Store
15 i interface{}
16 Sort []SortBy
17 Filter Filter
18 }
19
20 func (s *Store) NewSearch(i interface{}) *Search {
21 s.mutex.Lock()
22 defer s.mutex.Unlock()
23 if _, ok := s.types[typeName(i)]; !ok {
24 return nil
25 }
26 return &Search{
27 store: s,
28 i: i,
29 }
30 }
31
32 type PreparedSearch struct {
33 countStmt *sql.Stmt
34 getStmt *sql.Stmt
35 vars []interface{}
36 store *Store
37 }
38
39 func (s *Search) Prepare() (*PreparedSearch, error) {
40 var (
41 doneFirst bool
42 sql, sqlVars string
43 vars []interface{}
44 name = typeName(s.i)
45 )
46 if s.Filter != nil {
47 sql += "WHERE " + s.Filter.SQL() + " "
48 for _, i := range s.Filter.Vars() {
49 if v := reflect.ValueOf(i); v.Kind() != reflect.Ptr {
50 i = v.Addr().Interface()
51 }
52 if !isValidType(i) {
53 return nil, ErrInvalidType
54 }
55 vars = append(vars, i)
56 }
57 }
58 s.store.mutex.Lock()
59 defer s.store.mutex.Unlock()
60 count, err := s.store.db.Prepare("SELECT COUNT(1) FROM [" + name + "] " + sql)
61 if err != nil {
62 return nil, err
63 }
64 t := s.store.types[name]
65 for pos, f := range t.fields {
66 if pos == t.primary {
67 continue
68 }
69 if doneFirst {
70 sqlVars += ", "
71 } else {
72 doneFirst = true
73 }
74 sqlVars += "[" + f.name + "]"
75 }
76 if len(s.Sort) > 0 {
77 sql += "ORDER BY "
78 for n, f := range s.Sort {
79 if n > 0 {
80 sql += ", "
81 }
82 sql += "[" + f.Column + "]"
83 if f.Asc {
84 sql += " ASC "
85 } else {
86 sql += " DESC "
87 }
88 }
89 }
90 get, err := s.store.db.Prepare("SELECT [" + t.fields[t.primary].name + "] FROM [" + name + "] " + sql + "LIMIT ? OFFSET ?;")
91 if err != nil {
92 return nil, err
93 }
94 return &PreparedSearch{
95 count,
96 get,
97 vars,
98 s.store,
99 }, nil
100 }
101
102 func (p *PreparedSearch) Count() (int, error) {
103 p.store.mutex.Lock()
104 defer p.store.mutex.Unlock()
105 row := p.countStmt.QueryRow(p.getVars()...)
106 var count int
107 err := row.Scan(&count)
108 return count, err
109 }
110
111 func (p *PreparedSearch) GetPage(is []interface{}, offset int) (int, error) {
112 if len(is) == 0 {
113 return 0, nil
114 }
115 p.store.mutex.Lock()
116 defer p.store.mutex.Unlock()
117 rows, err := p.getStmt.Query(append(p.getVars(), len(is), offset)...)
118 if err != nil {
119 return 0, err
120 }
121 defer rows.Close()
122 return p.store.getPage(is, rows)
123 }
124
125 func (p *PreparedSearch) getVars() []interface{} {
126 vars := make([]interface{}, len(p.vars), len(p.vars)+2)
127 for n, v := range p.vars {
128 vars[n] = reflect.ValueOf(v).Elem().Interface()
129 }
130 return vars
131 }