1package couchdb 2 3import ( 4 "context" 5 "encoding/json" 6 "fmt" 7 8 "github.com/go-kivik/couchdb/chttp" 9 "github.com/go-kivik/kivik" 10 "github.com/go-kivik/kivik/driver" 11 "github.com/go-kivik/kivik/errors" 12) 13 14var findNotImplemented = errors.Status(kivik.StatusNotImplemented, "kivik: Find interface not implemented prior to CouchDB 2.0.0") 15 16func (d *db) CreateIndex(ctx context.Context, ddoc, name string, index interface{}) error { 17 if d.client.noFind || d.client.Compat == CompatCouch16 { 18 return findNotImplemented 19 } 20 indexObj, err := deJSONify(index) 21 if err != nil { 22 return err 23 } 24 parameters := struct { 25 Index interface{} `json:"index"` 26 Ddoc string `json:"ddoc,omitempty"` 27 Name string `json:"name,omitempty"` 28 }{ 29 Index: indexObj, 30 Ddoc: ddoc, 31 Name: name, 32 } 33 opts := &chttp.Options{ 34 Body: chttp.EncodeBody(parameters), 35 } 36 _, err = d.Client.DoError(ctx, kivik.MethodPost, d.path("_index", nil), opts) 37 return err 38} 39 40func (d *db) GetIndexes(ctx context.Context) ([]driver.Index, error) { 41 if d.client.noFind || d.client.Compat == CompatCouch16 { 42 return nil, findNotImplemented 43 } 44 var result struct { 45 Indexes []driver.Index `json:"indexes"` 46 } 47 _, err := d.Client.DoJSON(ctx, kivik.MethodGet, d.path("_index", nil), nil, &result) 48 return result.Indexes, err 49} 50 51func (d *db) DeleteIndex(ctx context.Context, ddoc, name string) error { 52 if d.client.noFind || d.client.Compat == CompatCouch16 { 53 return findNotImplemented 54 } 55 if ddoc == "" { 56 return missingArg("ddoc") 57 } 58 if name == "" { 59 return missingArg("name") 60 } 61 path := fmt.Sprintf("_index/%s/json/%s", ddoc, name) 62 _, err := d.Client.DoError(ctx, kivik.MethodDelete, d.path(path, nil), nil) 63 return err 64} 65 66func (d *db) Find(ctx context.Context, query interface{}) (driver.Rows, error) { 67 if d.client.noFind || d.client.Compat == CompatCouch16 { 68 return nil, findNotImplemented 69 } 70 opts := &chttp.Options{ 71 Body: chttp.EncodeBody(query), 72 } 73 resp, err := d.Client.DoReq(ctx, kivik.MethodPost, d.path("_find", nil), opts) 74 if err != nil { 75 return nil, err 76 } 77 if err = chttp.ResponseError(resp); err != nil { 78 return nil, err 79 } 80 return newRows(resp.Body), nil 81} 82 83type queryPlan struct { 84 DBName string `json:"dbname"` 85 Index map[string]interface{} `json:"index"` 86 Selector map[string]interface{} `json:"selector"` 87 Options map[string]interface{} `json:"opts"` 88 Limit int64 `json:"limit"` 89 Skip int64 `json:"skip"` 90 Fields fields `json:"fields"` 91 Range map[string]interface{} `json:"range"` 92} 93 94type fields []interface{} 95 96func (f *fields) UnmarshalJSON(data []byte) error { 97 if string(data) == `"all_fields"` { 98 return nil 99 } 100 var i []interface{} 101 if err := json.Unmarshal(data, &i); err != nil { 102 return err 103 } 104 newFields := make([]interface{}, len(i)) 105 copy(newFields, i) 106 *f = newFields 107 return nil 108} 109 110func (d *db) Explain(ctx context.Context, query interface{}) (*driver.QueryPlan, error) { 111 if d.client.noFind || d.client.Compat == CompatCouch16 { 112 return nil, findNotImplemented 113 } 114 opts := &chttp.Options{ 115 Body: chttp.EncodeBody(query), 116 } 117 var plan queryPlan 118 if _, err := d.Client.DoJSON(ctx, kivik.MethodPost, d.path("_explain", nil), opts, &plan); err != nil { 119 return nil, err 120 } 121 return &driver.QueryPlan{ 122 DBName: plan.DBName, 123 Index: plan.Index, 124 Selector: plan.Selector, 125 Options: plan.Options, 126 Limit: plan.Limit, 127 Skip: plan.Skip, 128 Fields: plan.Fields, 129 Range: plan.Range, 130 }, nil 131} 132