github.com/go-kivik/kivik/v4@v4.3.2/x/memorydb/find.go (about) 1 // Licensed under the Apache License, Version 2.0 (the "License"); you may not 2 // use this file except in compliance with the License. You may obtain a copy of 3 // the License at 4 // 5 // http://www.apache.org/licenses/LICENSE-2.0 6 // 7 // Unless required by applicable law or agreed to in writing, software 8 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 9 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 10 // License for the specific language governing permissions and limitations under 11 // the License. 12 13 package memorydb 14 15 import ( 16 "bytes" 17 "context" 18 "encoding/json" 19 "errors" 20 "io" 21 "net/http" 22 "strings" 23 24 "github.com/go-kivik/kivik/v4/driver" 25 "github.com/go-kivik/kivik/v4/x/mango" 26 ) 27 28 var errFindNotImplemented = errors.New("find feature not yet implemented") 29 30 type findQuery struct { 31 Selector *mango.Selector `json:"selector"` 32 Limit int64 `json:"limit"` 33 Skip int64 `json:"skip"` 34 Sort []string `json:"sort"` 35 Fields []string `json:"fields"` 36 UseIndex indexSpec `json:"use_index"` 37 } 38 39 type indexSpec struct { 40 ddoc string 41 index string 42 } 43 44 func (i *indexSpec) UnmarshalJSON(data []byte) error { 45 if data[0] == '"' { 46 return json.Unmarshal(data, &i.ddoc) 47 } 48 var values []string 49 if err := json.Unmarshal(data, &values); err != nil { 50 return err 51 } 52 const maxValues = 2 53 if len(values) == 0 || len(values) > maxValues { 54 return errors.New("invalid index specification") 55 } 56 i.ddoc = values[0] 57 if len(values) == maxValues { 58 i.index = values[1] 59 } 60 return nil 61 } 62 63 var _ driver.Finder = &db{} 64 65 func (d *db) CreateIndex(context.Context, string, string, interface{}, driver.Options) error { 66 return errFindNotImplemented 67 } 68 69 func (d *db) GetIndexes(context.Context, driver.Options) ([]driver.Index, error) { 70 return nil, errFindNotImplemented 71 } 72 73 func (d *db) DeleteIndex(context.Context, string, string, driver.Options) error { 74 return errFindNotImplemented 75 } 76 77 func (d *db) Find(ctx context.Context, query interface{}, _ driver.Options) (driver.Rows, error) { 78 if exists, _ := d.DBExists(ctx, d.dbName, nil); !exists { 79 return nil, statusError{status: http.StatusNotFound, error: errors.New("database does not exist")} 80 } 81 queryJSON, err := toJSON(query) 82 if err != nil { 83 return nil, err 84 } 85 fq := &findQuery{} 86 if err := json.NewDecoder(queryJSON).Decode(&fq); err != nil { 87 return nil, err 88 } 89 if fq == nil || fq.Selector == nil { 90 return nil, errors.New("Missing required key: selector") 91 } 92 fields := make(map[string]struct{}, len(fq.Fields)) 93 for _, field := range fq.Fields { 94 fields[field] = struct{}{} 95 } 96 rows := &findResults{ 97 resultSet: resultSet{ 98 docIDs: make([]string, 0), 99 revs: make([]*revision, 0), 100 }, 101 fields: fields, 102 } 103 for docID := range d.db.docs { 104 if doc, found := d.db.latestRevision(docID); found { 105 var cd couchDoc 106 if err := json.Unmarshal(doc.data, &cd); err != nil { 107 panic(err) 108 } 109 if fq.Selector.Match(map[string]interface{}(cd)) { 110 rows.docIDs = append(rows.docIDs, docID) 111 rows.revs = append(rows.revs, doc) 112 } 113 } 114 } 115 rows.offset = 0 116 rows.totalRows = int64(len(rows.docIDs)) 117 return rows, nil 118 } 119 120 func (d *db) Explain(context.Context, interface{}, driver.Options) (*driver.QueryPlan, error) { 121 return nil, errFindNotImplemented 122 } 123 124 type findResults struct { 125 resultSet 126 fields map[string]struct{} 127 } 128 129 var ( 130 _ driver.Rows = &findResults{} 131 _ driver.RowsWarner = &findResults{} 132 ) 133 134 func (r *findResults) Warning() string { 135 return "no matching index found, create an index to optimize query time" 136 } 137 138 func (r *findResults) Next(row *driver.Row) error { 139 if r.revs == nil || len(r.revs) == 0 { 140 return io.EOF 141 } 142 row.ID, r.docIDs = r.docIDs[0], r.docIDs[1:] 143 doc, err := r.filterDoc(r.revs[0].data) 144 if err != nil { 145 return err 146 } 147 row.Doc = bytes.NewReader(doc) 148 r.revs = r.revs[1:] 149 return nil 150 } 151 152 func (r *findResults) filterDoc(data []byte) ([]byte, error) { 153 if len(r.fields) == 0 { 154 return data, nil 155 } 156 var intermediateDoc map[string]interface{} 157 if err := json.Unmarshal(data, &intermediateDoc); err != nil { 158 return nil, err 159 } 160 for field := range intermediateDoc { 161 if _, ok := r.fields[field]; !ok { 162 delete(intermediateDoc, field) 163 } 164 } 165 return json.Marshal(intermediateDoc) 166 } 167 168 // toJSON converts a string, []byte, json.RawMessage, or an arbitrary type into 169 // an io.Reader of JSON marshaled data. 170 func toJSON(i interface{}) (io.Reader, error) { 171 switch t := i.(type) { 172 case string: 173 return strings.NewReader(t), nil 174 case []byte: 175 return bytes.NewReader(t), nil 176 case json.RawMessage: 177 return bytes.NewReader(t), nil 178 default: 179 buf := &bytes.Buffer{} 180 err := json.NewEncoder(buf).Encode(i) 181 return buf, err 182 } 183 }