1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package pouchdb
16
17 import (
18 "context"
19 "encoding/json"
20 "io"
21 "net/http"
22 "strings"
23
24 "github.com/gopherjs/gopherjs/js"
25
26 "github.com/go-kivik/kivik/v4/driver"
27 internal "github.com/go-kivik/kivik/v4/int/errors"
28 "github.com/go-kivik/kivik/v4/pouchdb/bindings"
29 )
30
31 var _ driver.Finder = &db{}
32
33
34
35 func buildIndex(ddoc, name string, index interface{}) (*js.Object, error) {
36 i, err := bindings.Objectify(index)
37 if err != nil {
38 return nil, err
39 }
40 o := js.Global.Get("Object").New(i)
41 if ddoc != "" {
42 o.Set("ddoc", ddoc)
43 }
44 if name != "" {
45 o.Set("name", name)
46 }
47 return o, nil
48 }
49
50 func (d *db) CreateIndex(ctx context.Context, ddoc, name string, index interface{}, _ driver.Options) error {
51 indexObj, err := buildIndex(ddoc, name, index)
52 if err != nil {
53 return err
54 }
55 _, err = d.db.CreateIndex(ctx, indexObj)
56 return err
57 }
58
59 func (d *db) GetIndexes(ctx context.Context, _ driver.Options) (indexes []driver.Index, err error) {
60 defer bindings.RecoverError(&err)
61 result, err := d.db.GetIndexes(ctx)
62 if err != nil {
63 return nil, err
64 }
65
66 var final struct {
67 Indexes []driver.Index `json:"indexes"`
68 }
69 err = json.Unmarshal([]byte(js.Global.Get("JSON").Call("stringify", result).String()), &final)
70 return final.Indexes, err
71 }
72
73
74 func (d *db) findIndex(ctx context.Context, ddoc, name string) (interface{}, error) {
75 ddoc = "_design/" + strings.TrimPrefix(ddoc, "_design/")
76 indexes, err := d.GetIndexes(ctx, nil)
77 if err != nil {
78 return nil, err
79 }
80 for _, idx := range indexes {
81 if idx.Type == "special" {
82 continue
83 }
84 if idx.DesignDoc == ddoc && idx.Name == name {
85 return map[string]interface{}{
86 "ddoc": idx.DesignDoc,
87 "name": idx.Name,
88 "type": idx.Type,
89 "def": idx.Definition,
90 }, nil
91 }
92 }
93 return nil, &internal.Error{Status: http.StatusNotFound, Message: "index does not exist"}
94 }
95
96 func (d *db) DeleteIndex(ctx context.Context, ddoc, name string, _ driver.Options) error {
97 index, err := d.findIndex(ctx, ddoc, name)
98 if err != nil {
99 return err
100 }
101 _, err = d.db.DeleteIndex(ctx, index)
102 return err
103 }
104
105 func (d *db) Find(ctx context.Context, query interface{}, _ driver.Options) (driver.Rows, error) {
106 result, err := d.db.Find(ctx, query)
107 if err != nil {
108 return nil, err
109 }
110 return &findRows{
111 Object: result,
112 }, nil
113 }
114
115 type findRows struct {
116 *js.Object
117 }
118
119 var _ driver.Rows = &findRows{}
120
121 func (r *findRows) Offset() int64 { return 0 }
122 func (r *findRows) TotalRows() int64 { return 0 }
123 func (r *findRows) UpdateSeq() string { return "" }
124 func (r *findRows) Warning() string {
125 if w := r.Get("warning"); w != js.Undefined {
126 return w.String()
127 }
128 return ""
129 }
130
131 func (r *findRows) Close() error {
132 r.Delete("docs")
133 return nil
134 }
135
136 func (r *findRows) Next(row *driver.Row) (err error) {
137 defer bindings.RecoverError(&err)
138 if r.Get("docs") == js.Undefined || r.Get("docs").Length() == 0 {
139 return io.EOF
140 }
141 next := r.Get("docs").Call("shift")
142 row.Doc = strings.NewReader(jsJSON.Call("stringify", next).String())
143 return nil
144 }
145
146 type queryPlan struct {
147 DBName string `json:"dbname"`
148 Index map[string]interface{} `json:"index"`
149 Selector map[string]interface{} `json:"selector"`
150 Options map[string]interface{} `json:"opts"`
151 Limit int64 `json:"limit"`
152 Skip int64 `json:"skip"`
153 Fields fields `json:"fields"`
154 Range map[string]interface{} `json:"range"`
155 }
156
157 type fields []interface{}
158
159 func (f *fields) UnmarshalJSON(data []byte) error {
160 if string(data) == `"all_fields"` {
161 return nil
162 }
163 var i []interface{}
164 if err := json.Unmarshal(data, &i); err != nil {
165 return err
166 }
167 newFields := make([]interface{}, len(i))
168 copy(newFields, i)
169 *f = newFields
170 return nil
171 }
172
173 func (d *db) Explain(ctx context.Context, query interface{}, _ driver.Options) (*driver.QueryPlan, error) {
174 result, err := d.db.Explain(ctx, query)
175 if err != nil {
176 return nil, err
177 }
178 planJSON := js.Global.Get("JSON").Call("stringify", result).String()
179 var plan queryPlan
180 if err := json.Unmarshal([]byte(planJSON), &plan); err != nil {
181 return nil, err
182 }
183 return &driver.QueryPlan{
184 DBName: plan.DBName,
185 Index: plan.Index,
186 Selector: plan.Selector,
187 Options: plan.Options,
188 Limit: plan.Limit,
189 Skip: plan.Skip,
190 Fields: plan.Fields,
191 Range: plan.Range,
192 }, nil
193 }
194
View as plain text