...

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

Documentation: github.com/go-kivik/kivik/v4/pouchdb

     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  //go:build js
    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  // buildIndex merges the ddoc and name into the index structure, as required
    34  // by the PouchDB-find plugin.
    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  	// This might not be the most efficient, but it's easy
    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  // findIndex attempts to find the requested index definition
    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") // Free up memory used by any remaining rows
   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