...

Source file src/github.com/go-kivik/kivik/v4/x/memorydb/find.go

Documentation: github.com/go-kivik/kivik/v4/x/memorydb

     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  }
   184  

View as plain text