forked from couchbase/go-couchbase
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathviews.go
More file actions
156 lines (137 loc) · 3.58 KB
/
views.go
File metadata and controls
156 lines (137 loc) · 3.58 KB
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
152
153
154
155
156
package couchbase
import (
"encoding/json"
"errors"
"fmt"
"math/rand"
"net/http"
"net/url"
)
type ViewRow struct {
ID string
Key interface{}
Value interface{}
Doc *interface{}
}
type ViewError struct {
From string
Reason string
}
func (ve ViewError) Error() string {
return fmt.Sprintf("Node: %v, reason: %v", ve.From, ve.Reason)
}
type ViewResult struct {
TotalRows int `json:"total_rows"`
Rows []ViewRow
Errors []ViewError
}
func (b *Bucket) randomBaseURL() (*url.URL, error) {
if len(b.Nodes) == 0 {
return nil, errors.New("no couch rest URLs")
}
node := b.Nodes[rand.Intn(len(b.Nodes))]
u, err := url.Parse(node.CouchAPIBase)
if err == nil && b.pool != nil {
u.User = b.pool.client.BaseURL.User
}
return u, err
}
// Document ID type for the startkey_docid parameter in views.
type DocId string
func qParam(k, v string) string {
format := `"%s"`
switch k {
case "startkey_docid", "stale":
format = "%s"
}
return fmt.Sprintf(format, v)
}
// Build a URL for a view with the given ddoc, view name, and
// parameters.
func (b *Bucket) ViewURL(ddoc, name string,
params map[string]interface{}) (string, error) {
u, err := b.randomBaseURL()
if err != nil {
return "", err
}
values := url.Values{}
for k, v := range params {
switch t := v.(type) {
case DocId:
values[k] = []string{string(t)}
case string:
values[k] = []string{qParam(k, t)}
case int:
values[k] = []string{fmt.Sprintf(`%d`, t)}
case bool:
values[k] = []string{fmt.Sprintf(`%v`, t)}
default:
b, err := json.Marshal(v)
if err != nil {
return "", fmt.Errorf("unsupported value-type %T in Query, "+
"json encoder said %v", t, err)
}
values[k] = []string{fmt.Sprintf(`%v`, string(b))}
}
}
if ddoc == "" && name == "_all_docs" {
u.Path = fmt.Sprintf("/%s/_all_docs", b.Name)
} else {
u.Path = fmt.Sprintf("/%s/_design/%s/_view/%s", b.Name, ddoc, name)
}
u.RawQuery = values.Encode()
return u.String(), nil
}
// Perform a view request that can map row values to a custom type.
//
// See the source to View for an example usage.
func (b *Bucket) ViewCustom(ddoc, name string, params map[string]interface{},
vres interface{}) error {
u, err := b.ViewURL(ddoc, name, params)
if err != nil {
return err
}
req, err := http.NewRequest("GET", u, nil)
if err != nil {
return err
}
maybeAddAuth(req, b.auth)
res, err := HttpClient.Do(req)
if err != nil {
return fmt.Errorf("Error starting view req at %v: %v", u, err)
}
defer res.Body.Close()
if res.StatusCode != 200 {
bod := make([]byte, 512)
l, _ := res.Body.Read(bod)
return fmt.Errorf("Error executing view req at %v: %v - %s",
u, res.Status, bod[:l])
}
d := json.NewDecoder(res.Body)
if err := d.Decode(vres); err != nil {
return err
}
return nil
}
// Execute a view.
//
// The ddoc parameter is just the bare name of your design doc without
// the "_design/" prefix.
//
// Parameters are string keys with values that correspond to couchbase
// view parameters. Primitive should work fairly naturally (booleans,
// ints, strings, etc...) and other values will attempt to be JSON
// marshaled (useful for array indexing on on view keys, for example).
//
// Example:
//
// res, err := couchbase.View("myddoc", "myview", map[string]interface{}{
// "group_level": 2,
// "start_key": []interface{}{"thing"},
// "end_key": []interface{}{"thing", map[string]string{}},
// "stale": false,
// })
func (b *Bucket) View(ddoc, name string, params map[string]interface{}) (ViewResult, error) {
vres := ViewResult{}
return vres, b.ViewCustom(ddoc, name, params, &vres)
}