func (im *IndexMapping) analyzerNameForPath(path string) string {
	for _, docMapping := range im.TypeMapping {
		analyzerName := docMapping.analyzerNameForPath(path)
		if analyzerName != "" {
			return analyzerName
		}
	}
	pathMapping := im.DefaultMapping.documentMappingForPath(path)
	if pathMapping != nil {
		if len(pathMapping.Fields) > 0 {
			if pathMapping.Fields[0].Analyzer != "" {
				return pathMapping.Fields[0].Analyzer
			}
		}
	}
	pathDecoded := decodePath(path)
	for _, docMapping := range im.TypeMapping {
		rv := docMapping.defaultAnalyzerName(pathDecoded)
		if rv != "" {
			return rv
		}
	}
	return im.DefaultAnalyzer
}
attempts to find the best analyzer to use with only a field name
will walk all the document types, look for field mappings at the
provided path, if one exists and it has an explicit analyzer
that is returned
nil should be an acceptable return value meaning we don't know
package bleve
import (
	"encoding/json"
	"fmt"
	"github.com/blevesearch/bleve/analysis"
	"github.com/blevesearch/bleve/analysis/analyzers/standard_analyzer"
	"github.com/blevesearch/bleve/analysis/datetime_parsers/datetime_optional"
	"github.com/blevesearch/bleve/document"
	"github.com/blevesearch/bleve/registry"
)
var MappingJSONStrict = false
const defaultTypeField = "_type"
const defaultType = "_default"
const defaultField = "_all"
const defaultAnalyzer = standard_analyzer.Name
const defaultDateTimeParser = datetime_optional.Name
type customAnalysis struct {
	CharFilters     map[string]map[string]interface{} `json:"char_filters,omitempty"`
	Tokenizers      map[string]map[string]interface{} `json:"tokenizers,omitempty"`
	TokenMaps       map[string]map[string]interface{} `json:"token_maps,omitempty"`
	TokenFilters    map[string]map[string]interface{} `json:"token_filters,omitempty"`
	Analyzers       map[string]map[string]interface{} `json:"analyzers,omitempty"`
	DateTimeParsers map[string]map[string]interface{} `json:"date_time_parsers,omitempty"`
}
func (c *customAnalysis) registerAll(i *IndexMapping) error {
	for name, config := range c.CharFilters {
		_, err := i.cache.DefineCharFilter(name, config)
		if err != nil {
			return err
		}
	}
	if len(c.Tokenizers) > 0 {
		todo := map[string]struct{}{}
		for name := range c.Tokenizers {
			todo[name] = struct{}{}
		}
		registered := 1
		errs := []error{}
		for len(todo) > 0 && registered > 0 {
			registered = 0
			errs = []error{}
			for name := range todo {
				config := c.Tokenizers[name]
				_, err := i.cache.DefineTokenizer(name, config)
				if err != nil {
					errs = append(errs, err)
				} else {
					delete(todo, name)
					registered++
				}
			}
		}
		if len(errs) > 0 {
			return errs[0]
		}
	}
	for name, config := range c.TokenMaps {
		_, err := i.cache.DefineTokenMap(name, config)
		if err != nil {
			return err
		}
	}
	for name, config := range c.TokenFilters {
		_, err := i.cache.DefineTokenFilter(name, config)
		if err != nil {
			return err
		}
	}
	for name, config := range c.Analyzers {
		_, err := i.cache.DefineAnalyzer(name, config)
		if err != nil {
			return err
		}
	}
	for name, config := range c.DateTimeParsers {
		_, err := i.cache.DefineDateTimeParser(name, config)
		if err != nil {
			return err
		}
	}
	return nil
}
func newCustomAnalysis() *customAnalysis {
	rv := customAnalysis{
		CharFilters:     make(map[string]map[string]interface{}),
		Tokenizers:      make(map[string]map[string]interface{}),
		TokenMaps:       make(map[string]map[string]interface{}),
		TokenFilters:    make(map[string]map[string]interface{}),
		Analyzers:       make(map[string]map[string]interface{}),
		DateTimeParsers: make(map[string]map[string]interface{}),
	}
	return &rv
}
type IndexMapping struct {
	TypeMapping           map[string]*DocumentMapping `json:"types,omitempty"`
	DefaultMapping        *DocumentMapping            `json:"default_mapping"`
	TypeField             string                      `json:"type_field"`
	DefaultType           string                      `json:"default_type"`
	DefaultAnalyzer       string                      `json:"default_analyzer"`
	DefaultDateTimeParser string                      `json:"default_datetime_parser"`
	DefaultField          string                      `json:"default_field"`
	StoreDynamic          bool                        `json:"store_dynamic"`
	IndexDynamic          bool                        `json:"index_dynamic"`
	CustomAnalysis        *customAnalysis             `json:"analysis,omitempty"`
	cache                 *registry.Cache
}
func (im *IndexMapping) AddCustomCharFilter(name string, config map[string]interface{}) error {
	_, err := im.cache.DefineCharFilter(name, config)
	if err != nil {
		return err
	}
	im.CustomAnalysis.CharFilters[name] = config
	return nil
}
func (im *IndexMapping) AddCustomTokenizer(name string, config map[string]interface{}) error {
	_, err := im.cache.DefineTokenizer(name, config)
	if err != nil {
		return err
	}
	im.CustomAnalysis.Tokenizers[name] = config
	return nil
}
func (im *IndexMapping) AddCustomTokenMap(name string, config map[string]interface{}) error {
	_, err := im.cache.DefineTokenMap(name, config)
	if err != nil {
		return err
	}
	im.CustomAnalysis.TokenMaps[name] = config
	return nil
}
func (im *IndexMapping) AddCustomTokenFilter(name string, config map[string]interface{}) error {
	_, err := im.cache.DefineTokenFilter(name, config)
	if err != nil {
		return err
	}
	im.CustomAnalysis.TokenFilters[name] = config
	return nil
}
func (im *IndexMapping) AddCustomAnalyzer(name string, config map[string]interface{}) error {
	_, err := im.cache.DefineAnalyzer(name, config)
	if err != nil {
		return err
	}
	im.CustomAnalysis.Analyzers[name] = config
	return nil
}
func (im *IndexMapping) AddCustomDateTimeParser(name string, config map[string]interface{}) error {
	_, err := im.cache.DefineDateTimeParser(name, config)
	if err != nil {
		return err
	}
	im.CustomAnalysis.DateTimeParsers[name] = config
	return nil
}
func NewIndexMapping() *IndexMapping {
	return &IndexMapping{
		TypeMapping:           make(map[string]*DocumentMapping),
		DefaultMapping:        NewDocumentMapping(),
		TypeField:             defaultTypeField,
		DefaultType:           defaultType,
		DefaultAnalyzer:       defaultAnalyzer,
		DefaultDateTimeParser: defaultDateTimeParser,
		DefaultField:          defaultField,
		IndexDynamic:          IndexDynamic,
		StoreDynamic:          StoreDynamic,
		CustomAnalysis:        newCustomAnalysis(),
		cache:                 registry.NewCache(),
	}
}
func (im *IndexMapping) Validate() error {
	_, err := im.cache.AnalyzerNamed(im.DefaultAnalyzer)
	if err != nil {
		return err
	}
	_, err = im.cache.DateTimeParserNamed(im.DefaultDateTimeParser)
	if err != nil {
		return err
	}
	err = im.DefaultMapping.Validate(im.cache)
	if err != nil {
		return err
	}
	for _, docMapping := range im.TypeMapping {
		err = docMapping.Validate(im.cache)
		if err != nil {
			return err
		}
	}
	return nil
}
func (im *IndexMapping) AddDocumentMapping(doctype string, dm *DocumentMapping) {
	im.TypeMapping[doctype] = dm
}
func (im *IndexMapping) mappingForType(docType string) *DocumentMapping {
	docMapping := im.TypeMapping[docType]
	if docMapping == nil {
		docMapping = im.DefaultMapping
	}
	return docMapping
}
func (im *IndexMapping) UnmarshalJSON(data []byte) error {
	var tmp map[string]json.RawMessage
	err := json.Unmarshal(data, &tmp)
	if err != nil {
		return err
	}
	im.cache = registry.NewCache()
	im.CustomAnalysis = newCustomAnalysis()
	im.TypeField = defaultTypeField
	im.DefaultType = defaultType
	im.DefaultAnalyzer = defaultAnalyzer
	im.DefaultDateTimeParser = defaultDateTimeParser
	im.DefaultField = defaultField
	im.DefaultMapping = NewDocumentMapping()
	im.TypeMapping = make(map[string]*DocumentMapping)
	im.StoreDynamic = StoreDynamic
	im.IndexDynamic = IndexDynamic
	var invalidKeys []string
	for k, v := range tmp {
		switch k {
		case "analysis":
			err := json.Unmarshal(v, &im.CustomAnalysis)
			if err != nil {
				return err
			}
		case "type_field":
			err := json.Unmarshal(v, &im.TypeField)
			if err != nil {
				return err
			}
		case "default_type":
			err := json.Unmarshal(v, &im.DefaultType)
			if err != nil {
				return err
			}
		case "default_analyzer":
			err := json.Unmarshal(v, &im.DefaultAnalyzer)
			if err != nil {
				return err
			}
		case "default_datetime_parser":
			err := json.Unmarshal(v, &im.DefaultDateTimeParser)
			if err != nil {
				return err
			}
		case "default_field":
			err := json.Unmarshal(v, &im.DefaultField)
			if err != nil {
				return err
			}
		case "default_mapping":
			err := json.Unmarshal(v, &im.DefaultMapping)
			if err != nil {
				return err
			}
		case "types":
			err := json.Unmarshal(v, &im.TypeMapping)
			if err != nil {
				return err
			}
		case "store_dynamic":
			err := json.Unmarshal(v, &im.StoreDynamic)
			if err != nil {
				return err
			}
		case "index_dynamic":
			err := json.Unmarshal(v, &im.IndexDynamic)
			if err != nil {
				return err
			}
		default:
			invalidKeys = append(invalidKeys, k)
		}
	}
	if MappingJSONStrict && len(invalidKeys) > 0 {
		return fmt.Errorf("index mapping contains invalid keys: %v", invalidKeys)
	}
	err = im.CustomAnalysis.registerAll(im)
	if err != nil {
		return err
	}
	return nil
}
func (im *IndexMapping) determineType(data interface{}) string {
	classifier, ok := data.(Classifier)
	if ok {
		return classifier.Type()
	}
	typ, ok := mustString(lookupPropertyPath(data, im.TypeField))
	if ok {
		return typ
	}
	return im.DefaultType
}
func (im *IndexMapping) mapDocument(doc *document.Document, data interface{}) error {
	docType := im.determineType(data)
	docMapping := im.mappingForType(docType)
	walkContext := im.newWalkContext(doc, docMapping)
	if docMapping.Enabled {
		docMapping.walkDocument(data, []string{}, []uint64{}, walkContext)
		allMapping := docMapping.documentMappingForPath("_all")
		if allMapping == nil || (allMapping.Enabled != false) {
			field := document.NewCompositeFieldWithIndexingOptions("_all", true, []string{}, walkContext.excludedFromAll, document.IndexField|document.IncludeTermVectors)
			doc.AddField(field)
		}
	}
	return nil
}
type walkContext struct {
	doc             *document.Document
	im              *IndexMapping
	dm              *DocumentMapping
	excludedFromAll []string
}
func (im *IndexMapping) newWalkContext(doc *document.Document, dm *DocumentMapping) *walkContext {
	return &walkContext{
		doc:             doc,
		im:              im,
		dm:              dm,
		excludedFromAll: []string{},
	}
}