func (i *indexImpl) Fields() (fields []string, err error) {
	i.mutex.RLock()
	defer i.mutex.RUnlock()
	if !i.open {
		return nil, ErrorIndexClosed
	}
	indexReader, err := i.i.Reader()
	if err != nil {
		return nil, err
	}
	defer func() {
		if cerr := indexReader.Close(); err == nil && cerr != nil {
			err = cerr
		}
	}()
	fields, err = indexReader.Fields()
	if err != nil {
		return nil, err
	}
	return fields, nil
}
Fields returns the name of all the fields this
Index has operated on.
package bleve
import (
	"encoding/json"
	"fmt"
	"os"
	"sync"
	"sync/atomic"
	"time"
	"golang.org/x/net/context"
	"github.com/blevesearch/bleve/document"
	"github.com/blevesearch/bleve/index"
	"github.com/blevesearch/bleve/index/store"
	"github.com/blevesearch/bleve/index/upside_down"
	"github.com/blevesearch/bleve/registry"
	"github.com/blevesearch/bleve/search"
	"github.com/blevesearch/bleve/search/collectors"
	"github.com/blevesearch/bleve/search/facets"
	"github.com/blevesearch/bleve/search/highlight"
)
type indexImpl struct {
	path  string
	name  string
	meta  *indexMeta
	i     index.Index
	m     *IndexMapping
	mutex sync.RWMutex
	open  bool
	stats *IndexStat
}
const storePath = "store"
var mappingInternalKey = []byte("_mapping")
func indexStorePath(path string) string {
	return path + string(os.PathSeparator) + storePath
}
func newIndexUsing(path string, mapping *IndexMapping, indexType string, kvstore string, kvconfig map[string]interface{}) (*indexImpl, error) {
	err := mapping.Validate()
	if err != nil {
		return nil, err
	}
	if kvconfig == nil {
		kvconfig = map[string]interface{}{}
	}
	if kvstore == "" {
		return nil, fmt.Errorf("bleve not configured for file based indexing")
	}
	rv := indexImpl{
		path: path,
		name: path,
		m:    mapping,
		meta: newIndexMeta(indexType, kvstore, kvconfig),
	}
	rv.stats = &IndexStat{i: &rv}
	if path != "" {
		err = rv.meta.Save(path)
		if err != nil {
			return nil, err
		}
		kvconfig["create_if_missing"] = true
		kvconfig["error_if_exists"] = true
		kvconfig["path"] = indexStorePath(path)
	} else {
		kvconfig["path"] = ""
	}
	indexTypeConstructor := registry.IndexTypeConstructorByName(rv.meta.IndexType)
	if indexTypeConstructor == nil {
		return nil, ErrorUnknownIndexType
	}
	rv.i, err = indexTypeConstructor(rv.meta.Storage, kvconfig, Config.analysisQueue)
	if err != nil {
		return nil, err
	}
	err = rv.i.Open()
	if err != nil {
		if err == index.ErrorUnknownStorageType {
			return nil, ErrorUnknownStorageType
		}
		return nil, err
	}
	mappingBytes, err := json.Marshal(mapping)
	if err != nil {
		return nil, err
	}
	err = rv.i.SetInternal(mappingInternalKey, mappingBytes)
	if err != nil {
		return nil, err
	}
	rv.mutex.Lock()
	defer rv.mutex.Unlock()
	rv.open = true
	indexStats.Register(&rv)
	return &rv, nil
}
func openIndexUsing(path string, runtimeConfig map[string]interface{}) (rv *indexImpl, err error) {
	rv = &indexImpl{
		path: path,
		name: path,
	}
	rv.stats = &IndexStat{i: rv}
	rv.meta, err = openIndexMeta(path)
	if err != nil {
		return nil, err
	}
	if rv.meta.IndexType == "" {
		rv.meta.IndexType = upside_down.Name
	}
	storeConfig := rv.meta.Config
	if storeConfig == nil {
		storeConfig = map[string]interface{}{}
	}
	storeConfig["path"] = indexStorePath(path)
	storeConfig["create_if_missing"] = false
	storeConfig["error_if_exists"] = false
	for rck, rcv := range runtimeConfig {
		storeConfig[rck] = rcv
	}
	indexTypeConstructor := registry.IndexTypeConstructorByName(rv.meta.IndexType)
	if indexTypeConstructor == nil {
		return nil, ErrorUnknownIndexType
	}
	rv.i, err = indexTypeConstructor(rv.meta.Storage, storeConfig, Config.analysisQueue)
	if err != nil {
		return nil, err
	}
	err = rv.i.Open()
	if err != nil {
		if err == index.ErrorUnknownStorageType {
			return nil, ErrorUnknownStorageType
		}
		return nil, err
	}
	indexReader, err := rv.i.Reader()
	if err != nil {
		return nil, err
	}
	defer func() {
		if cerr := indexReader.Close(); cerr != nil && err == nil {
			err = cerr
		}
	}()
	mappingBytes, err := indexReader.GetInternal(mappingInternalKey)
	if err != nil {
		return nil, err
	}
	var im IndexMapping
	err = json.Unmarshal(mappingBytes, &im)
	if err != nil {
		return nil, fmt.Errorf("error parsing mapping JSON: %v\nmapping contents:\n%s", err, string(mappingBytes))
	}
	rv.mutex.Lock()
	defer rv.mutex.Unlock()
	rv.open = true
	err = im.Validate()
	if err != nil {
		return rv, err
	}
	rv.m = &im
	indexStats.Register(rv)
	return rv, err
}
func (i *indexImpl) Advanced() (index.Index, store.KVStore, error) {
	s, err := i.i.Advanced()
	if err != nil {
		return nil, nil, err
	}
	return i.i, s, nil
}
func (i *indexImpl) Mapping() *IndexMapping {
	return i.m
}
func (i *indexImpl) Index(id string, data interface{}) (err error) {
	if id == "" {
		return ErrorEmptyID
	}
	i.mutex.RLock()
	defer i.mutex.RUnlock()
	if !i.open {
		return ErrorIndexClosed
	}
	doc := document.NewDocument(id)
	err = i.m.mapDocument(doc, data)
	if err != nil {
		return
	}
	err = i.i.Update(doc)
	return
}
func (i *indexImpl) Delete(id string) (err error) {
	if id == "" {
		return ErrorEmptyID
	}
	i.mutex.RLock()
	defer i.mutex.RUnlock()
	if !i.open {
		return ErrorIndexClosed
	}
	err = i.i.Delete(id)
	return
}
func (i *indexImpl) Batch(b *Batch) error {
	i.mutex.RLock()
	defer i.mutex.RUnlock()
	if !i.open {
		return ErrorIndexClosed
	}
	return i.i.Batch(b.internal)
}
func (i *indexImpl) Document(id string) (doc *document.Document, err error) {
	i.mutex.RLock()
	defer i.mutex.RUnlock()
	if !i.open {
		return nil, ErrorIndexClosed
	}
	indexReader, err := i.i.Reader()
	if err != nil {
		return nil, err
	}
	defer func() {
		if cerr := indexReader.Close(); err == nil && cerr != nil {
			err = cerr
		}
	}()
	doc, err = indexReader.Document(id)
	if err != nil {
		return nil, err
	}
	return doc, nil
}
func (i *indexImpl) DocCount() (count uint64, err error) {
	i.mutex.RLock()
	defer i.mutex.RUnlock()
	if !i.open {
		return 0, ErrorIndexClosed
	}
	indexReader, err := i.i.Reader()
	if err != nil {
		return 0, fmt.Errorf("error opening index reader %v", err)
	}
	defer func() {
		if cerr := indexReader.Close(); err == nil && cerr != nil {
			err = cerr
		}
	}()
	count, err = indexReader.DocCount()
	return
}
func (i *indexImpl) Search(req *SearchRequest) (sr *SearchResult, err error) {
	return i.SearchInContext(context.Background(), req)
}
func (i *indexImpl) SearchInContext(ctx context.Context, req *SearchRequest) (sr *SearchResult, err error) {
	i.mutex.RLock()
	defer i.mutex.RUnlock()
	searchStart := time.Now()
	if !i.open {
		return nil, ErrorIndexClosed
	}
	collector := collectors.NewTopNCollector(req.Size, req.From, req.Sort)
	indexReader, err := i.i.Reader()
	if err != nil {
		return nil, fmt.Errorf("error opening index reader %v", err)
	}
	defer func() {
		if cerr := indexReader.Close(); err == nil && cerr != nil {
			err = cerr
		}
	}()
	searcher, err := req.Query.Searcher(indexReader, i.m, req.Explain)
	if err != nil {
		return nil, err
	}
	defer func() {
		if serr := searcher.Close(); err == nil && serr != nil {
			err = serr
		}
	}()
	if req.Facets != nil {
		facetsBuilder := search.NewFacetsBuilder(indexReader)
		for facetName, facetRequest := range req.Facets {
			if facetRequest.NumericRanges != nil {
				facetBuilder := facets.NewNumericFacetBuilder(facetRequest.Field, facetRequest.Size)
				for _, nr := range facetRequest.NumericRanges {
					facetBuilder.AddRange(nr.Name, nr.Min, nr.Max)
				}
				facetsBuilder.Add(facetName, facetBuilder)
			} else if facetRequest.DateTimeRanges != nil {
				facetBuilder := facets.NewDateTimeFacetBuilder(facetRequest.Field, facetRequest.Size)
				dateTimeParser := i.m.dateTimeParserNamed(i.m.DefaultDateTimeParser)
				for _, dr := range facetRequest.DateTimeRanges {
					dr.ParseDates(dateTimeParser)
					facetBuilder.AddRange(dr.Name, dr.Start, dr.End)
				}
				facetsBuilder.Add(facetName, facetBuilder)
			} else {
				facetBuilder := facets.NewTermsFacetBuilder(facetRequest.Field, facetRequest.Size)
				facetsBuilder.Add(facetName, facetBuilder)
			}
		}
		collector.SetFacetsBuilder(facetsBuilder)
	}
	err = collector.Collect(ctx, searcher, indexReader)
	if err != nil {
		return nil, err
	}
	hits := collector.Results()
	var highlighter highlight.Highlighter
	if req.Highlight != nil {
		highlighter, err = Config.Cache.HighlighterNamed(Config.DefaultHighlighter)
		if err != nil {
			return nil, err
		}
		if req.Highlight.Style != nil {
			highlighter, err = Config.Cache.HighlighterNamed(*req.Highlight.Style)
			if err != nil {
				return nil, err
			}
		}
		if highlighter == nil {
			return nil, fmt.Errorf("no highlighter named `%s` registered", *req.Highlight.Style)
		}
	}
	for _, hit := range hits {
		if len(req.Fields) > 0 || highlighter != nil {
			doc, err := indexReader.Document(hit.ID)
			if err == nil && doc != nil {
				if len(req.Fields) > 0 {
					for _, f := range req.Fields {
						for _, docF := range doc.Fields {
							if f == "*" || docF.Name() == f {
								var value interface{}
								switch docF := docF.(type) {
								case *document.TextField:
									value = string(docF.Value())
								case *document.NumericField:
									num, err := docF.Number()
									if err == nil {
										value = num
									}
								case *document.DateTimeField:
									datetime, err := docF.DateTime()
									if err == nil {
										value = datetime.Format(time.RFC3339)
									}
								case *document.BooleanField:
									boolean, err := docF.Boolean()
									if err == nil {
										value = boolean
									}
								}
								if value != nil {
									hit.AddFieldValue(docF.Name(), value)
								}
							}
						}
					}
				}
				if highlighter != nil {
					highlightFields := req.Highlight.Fields
					if highlightFields == nil {
						highlightFields = make([]string, 0, len(hit.Locations))
						for k := range hit.Locations {
							highlightFields = append(highlightFields, k)
						}
					}
					for _, hf := range highlightFields {
						highlighter.BestFragmentsInField(hit, doc, hf, 1)
					}
				}
			} else if doc == nil {
				return nil, ErrorIndexReadInconsistency
			}
		}
		if i.name != "" {
			hit.Index = i.name
		}
	}
	atomic.AddUint64(&i.stats.searches, 1)
	searchDuration := time.Since(searchStart)
	atomic.AddUint64(&i.stats.searchTime, uint64(searchDuration))
	if searchDuration > Config.SlowSearchLogThreshold {
		logger.Printf("slow search took %s - %v", searchDuration, req)
	}
	return &SearchResult{
		Status: &SearchStatus{
			Total:      1,
			Failed:     0,
			Successful: 1,
			Errors:     make(map[string]error),
		},
		Request:  req,
		Hits:     hits,
		Total:    collector.Total(),
		MaxScore: collector.MaxScore(),
		Took:     searchDuration,
		Facets:   collector.FacetResults(),
	}, nil
}