<commit_before>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")
	}
	nodeNo := rand.Intn(len(b.Nodes))
	node := b.Nodes[nodeNo]
	u, err := ParseURL(node.CouchAPIBase)
	if err != nil {
		return nil, fmt.Errorf("Config error: Bucket %q node #%d CouchAPIBase=%q: %v",
			b.Name, nodeNo, node.CouchAPIBase, err)
	} else if 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.authHandler())

	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)
}
<commit_msg>Return descriptive error on attempt to query views of non-healthy bucket<commit_after>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")
	}
	nodeNo := rand.Intn(len(b.Nodes))
	node := b.Nodes[nodeNo]
	if node.CouchAPIBase == "" {
		// Probably in "warmup" state
		return nil, fmt.Errorf("Bucket is in %q state, not ready for view queries", node.Status)
	}
	u, err := ParseURL(node.CouchAPIBase)
	if err != nil {
		return nil, fmt.Errorf("Config error: Bucket %q node #%d CouchAPIBase=%q: %v",
			b.Name, nodeNo, node.CouchAPIBase, err)
	} else if 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.authHandler())

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

{'fraction_non_alphanumeric': 0.1656789812129618, 'fraction_numerical': 0.002017400075652503, 'mean_word_length': 3.33205898416166, 'pattern_counts': {'":': 10, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7496110', 'n_tokens_mistral': 2994, 'n_tokens_neox': 2856, 'n_words': 875}