// Copyright 2012-2015 Oliver Eilhard. All rights reserved. // Use of this source code is governed by a MIT-license. // See http://olivere.mit-license.org/license.txt for details. package elastic import ( "bytes" "encoding/json" "errors" "fmt" "log" "math/rand" "net/http" "net/http/httputil" "net/url" "regexp" "strings" "sync" "time" ) const ( // Version is the current version of Elastic. Version = "2.0.0" // DefaultUrl is the default endpoint of Elasticsearch on the local machine. // It is used e.g. when initializing a new Client without a specific URL. DefaultURL = "http://127.0.0.1:9200" // DefaultScheme is the default protocol scheme to use when sniffing // the Elasticsearch cluster. DefaultScheme = "http" // DefaultHealthcheckEnabled specifies if healthchecks are enabled by default. DefaultHealthcheckEnabled = true // DefaultHealthcheckInterval is the default interval between // two health checks of the nodes in the cluster. DefaultHealthcheckInterval = 60 * time.Second // DefaultSnifferEnabled specifies if the sniffer is enabled by default. DefaultSnifferEnabled = true // DefaultSnifferInterval is the interval between two sniffing procedures, // i.e. the lookup of all nodes in the cluster and their addition/removal // from the list of actual connections. DefaultSnifferInterval = 15 * time.Minute // DefaultSnifferTimeout is the default timeout after which the // sniffing process times out. DefaultSnifferTimeout = 1 * time.Second // DefaultMaxRetries is the number of retries for a single request after // Elastic will give up and return an error. It is zero by default, so // retry is disabled by default. DefaultMaxRetries = 0 ) var ( // ErrNoClient is raised when no Elasticsearch node is available. ErrNoClient = errors.New("no Elasticsearch node available") // ErrRetry is raised when a request cannot be executed after the configured // number of retries. ErrRetry = errors.New("cannot connect after several retries") ) // ClientOptionFunc is a function that configures a Client. // It is used in NewClient. type ClientOptionFunc func(*Client) error // Client is an Elasticsearch client. Create one by calling NewClient. type Client struct { c *http.Client // net/http Client to use for requests connsMu sync.RWMutex // connsMu guards the next block conns []*conn // all connections cindex int // index into conns mu sync.RWMutex // guards the next block urls []string // set of URLs passed initially to the client running bool // true if the client's background processes are running errorlog *log.Logger // error log for critical messages infolog *log.Logger // information log for e.g. response times tracelog *log.Logger // trace log for debugging maxRetries int // max. number of retries scheme string // http or https healthcheckEnabled bool // healthchecks enabled or disabled healthcheckInterval time.Duration // interval between healthchecks healthcheckStop chan bool // notify healthchecker to stop, and notify back snifferEnabled bool // sniffer enabled or disabled snifferTimeout time.Duration // time the sniffer waits for a response from nodes info API snifferInterval time.Duration // interval between sniffing snifferStop chan bool // notify sniffer to stop, and notify back decoder Decoder // used to decode data sent from Elasticsearch } // NewClient creates a new client to work with Elasticsearch. // // The caller can configure the new client by passing configuration options // to the func. // // Example: // // client, err := elastic.NewClient( // elastic.SetURL("http://localhost:9200", "http://localhost:9201"), // elastic.SetMaxRetries(10)) // // If no URL is configured, Elastic uses DefaultURL by default. // // If the sniffer is enabled (the default), the new client then sniffes // the cluster via the Nodes Info API // (see http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/cluster-nodes-info.html#cluster-nodes-info). // It uses the URLs specified by the caller. The caller is responsible // to only pass a list of URLs of nodes that belong to the same cluster. // This sniffing process is run on startup and periodically. // Use SnifferInterval to set the interval between two sniffs (default is // 15 minutes). In other words: By default, the client will find new nodes // in the cluster and remove those that are no longer available every // 15 minutes. Disable the sniffer by passing SetSniff(false) to NewClient. // // The list of nodes found in the sniffing process will be used to make // connections to the REST API of Elasticsearch. These nodes are also // periodically checked in a shorter time frame. This process is called // a health check. By default, a health check is done every 60 seconds. // You can set a shorter or longer interval by SetHealthcheckInterval. // Disabling health checks is not recommended, but can be done by // SetHealthcheck(false). // // Connections are automatically marked as dead or healthy while // making requests to Elasticsearch. When a request fails, Elastic will // retry up to a maximum number of retries configured with SetMaxRetries. // Retries are disabled by default. // // If no HttpClient is configured, then http.DefaultClient is used. // You can use your own http.Client with some http.Transport for // advanced scenarios. // // An error is also returned when some configuration option is invalid or // the new client cannot sniff the cluster (if enabled). func NewClient(options ...ClientOptionFunc) (*Client, error) { // Set up the client c := &Client{ urls: []string{DefaultURL}, c: http.DefaultClient, conns: make([]*conn, 0), cindex: -1, scheme: DefaultScheme, decoder: &DefaultDecoder{}, maxRetries: DefaultMaxRetries, healthcheckEnabled: DefaultHealthcheckEnabled, healthcheckInterval: DefaultHealthcheckInterval, healthcheckStop: make(chan bool), snifferEnabled: DefaultSnifferEnabled, snifferInterval: DefaultSnifferInterval, snifferStop: make(chan bool), snifferTimeout: DefaultSnifferTimeout, } // Run the options on it for _, option := range options { if err := option(c); err != nil { return nil, err } } if len(c.urls) == 0 { c.urls = []string{DefaultURL} } c.urls = canonicalize(c.urls...) if c.snifferEnabled { // Sniff the cluster initially if err := c.sniff(); err != nil { return nil, err } } else { // Do not sniff the cluster initially. Use the provided URLs instead. for _, url := range c.urls { c.conns = append(c.conns, newConn(url, url)) } } // Perform an initial health check c.healthcheck() go c.sniffer() // periodically update cluster information go c.healthchecker() // start goroutine periodically ping all nodes of the cluster c.mu.Lock() c.running = true c.mu.Unlock() return c, nil } // SetHttpClient can be used to specify the http.Client to use when making // HTTP requests to Elasticsearch. func SetHttpClient(httpClient *http.Client) ClientOptionFunc { return func(c *Client) error { if httpClient != nil { c.c = httpClient } else { c.c = http.DefaultClient } return nil } } // SetURL defines the URL endpoints of the Elasticsearch nodes. Notice that // when sniffing is enabled, these URLs are used to initially sniff the // cluster on startup. func SetURL(urls ...string) ClientOptionFunc { return func(c *Client) error { switch len(urls) { case 0: c.urls = []string{DefaultURL} default: c.urls = make([]string, 0) for _, url := range urls { c.urls = append(c.urls, url) } } return nil } } // SetScheme sets the HTTP scheme to look for when sniffing (http or https). // This is http by default. func SetScheme(scheme string) ClientOptionFunc { return func(c *Client) error { c.scheme = scheme return nil } } // SetSniff enables or disables the sniffer (enabled by default). func SetSniff(enabled bool) ClientOptionFunc { return func(c *Client) error { c.snifferEnabled = enabled return nil } } // SetSnifferInterval sets the interval between two sniffing processes. // The default interval is 15 minutes. func SetSnifferInterval(interval time.Duration) ClientOptionFunc { return func(c *Client) error { c.snifferInterval = interval return nil } } // SetSnifferTimeout sets the timeout for the sniffer that finds the // nodes in a cluster. The default is 1 second. func SetSnifferTimeout(timeout time.Duration) ClientOptionFunc { return func(c *Client) error { c.snifferTimeout = timeout return nil } } // SetHealthcheck enables or disables healthchecks (enabled by default). func SetHealthcheck(enabled bool) ClientOptionFunc { return func(c *Client) error { c.healthcheckEnabled = enabled return nil } } // SetHealthcheckInterval sets the interval between two health checks. // The default interval is 60 seconds. func SetHealthcheckInterval(interval time.Duration) ClientOptionFunc { return func(c *Client) error { c.healthcheckInterval = interval return nil } } // SetMaxRetries sets the maximum number of retries before giving up when // performing a HTTP request to Elasticsearch. func SetMaxRetries(maxRetries int) func(*Client) error { return func(c *Client) error { if maxRetries < 0 { return errors.New("MaxRetries must be greater than or equal to 0") } c.maxRetries = maxRetries return nil } } // SetDecoder sets the Decoder to use when decoding data from Elasticsearch. // DefaultDecoder is used by default. func SetDecoder(decoder Decoder) func(*Client) error { return func(c *Client) error { if decoder != nil { c.decoder = decoder } else { c.decoder = &DefaultDecoder{} } return nil } } // SetErrorLog sets the logger for critical messages like nodes joining // or leaving the cluster or failing requests. It is nil by default. func SetErrorLog(logger *log.Logger) func(*Client) error { return func(c *Client) error { c.errorlog = logger return nil } } // SetInfoLog sets the logger for informational messages, e.g. requests // and their response times. It is nil by default. func SetInfoLog(logger *log.Logger) func(*Client) error { return func(c *Client) error { c.infolog = logger return nil } } // SetTraceLog specifies the log.Logger to use for output of HTTP requests // and responses which is helpful during debugging. It is nil by default. func SetTraceLog(logger *log.Logger) func(*Client) error { return func(c *Client) error { c.tracelog = logger return nil } } // String returns a string representation of the client status. func (c *Client) String() string { c.connsMu.Lock() conns := c.conns c.connsMu.Unlock() var buf bytes.Buffer for i, conn := range conns { if i > 0 { buf.WriteString(", ") } buf.WriteString(conn.String()) } return buf.String() } // IsRunning returns true if the background processes of the client are // running, false otherwise. func (c *Client) IsRunning() bool { c.mu.RLock() defer c.mu.RUnlock() return c.running } // Start starts the background processes like sniffing the cluster and // periodic health checks. You don't need to run Start when creating a // client with NewClient; the background processes are run by default. // // If the background processes are already running, this is a no-op. func (c *Client) Start() { c.mu.RLock() if c.running { c.mu.RUnlock() return } c.mu.RUnlock() go c.sniffer() go c.healthchecker() c.mu.Lock() c.running = true c.mu.Unlock() c.infof("elastic: client started") } // Stop stops the background processes that the client is running, // i.e. sniffing the cluster periodically and running health checks // on the nodes. // // If the background processes are not running, this is a no-op. func (c *Client) Stop() { c.mu.RLock() if !c.running { c.mu.RUnlock() return } c.mu.RUnlock() c.healthcheckStop <- true <-c.healthcheckStop c.snifferStop <- true <-c.snifferStop c.mu.Lock() c.running = false c.mu.Unlock() c.infof("elastic: client stopped") } // errorf logs to the error log. func (c *Client) errorf(format string, args ...interface{}) { if c.errorlog != nil { c.errorlog.Printf(format, args...) } } // infof logs informational messages. func (c *Client) infof(format string, args ...interface{}) { if c.infolog != nil { c.infolog.Printf(format, args...) } } // tracef logs to the trace log. func (c *Client) tracef(format string, args ...interface{}) { if c.tracelog != nil { c.tracelog.Printf(format, args...) } } // dumpRequest dumps the given HTTP request to the trace log. func (c *Client) dumpRequest(r *http.Request) { if c.tracelog != nil { out, err := httputil.DumpRequestOut(r, true) if err == nil { c.tracef("%s\n", string(out)) } } } // dumpResponse dumps the given HTTP response to the trace log. func (c *Client) dumpResponse(resp *http.Response) { if c.tracelog != nil { out, err := httputil.DumpResponse(resp, true) if err == nil { c.tracef("%s\n", string(out)) } } } // sniffer periodically runs sniff. func (c *Client) sniffer() { for { c.mu.RLock() ticker := time.NewTicker(c.snifferInterval) c.mu.RUnlock() select { case <-c.snifferStop: // we are asked to stop, so we signal back that we're stopping now c.snifferStop <- true return case <-ticker.C: c.sniff() } } } // sniff uses the Node Info API to return the list of nodes in the cluster. // It uses the list of URLs passed on startup plus the list of URLs found // by the preceding sniffing process (if sniffing is enabled). // // If sniffing is disabled, this is a no-op. func (c *Client) sniff() error { c.mu.RLock() if !c.snifferEnabled { c.mu.RUnlock() return nil } // Use all available URLs provided to sniff the cluster. urlsMap := make(map[string]bool) urls := make([]string, 0) // Add all URLs provided on startup for _, url := range c.urls { urlsMap[url] = true urls = append(urls, url) } timeout := c.snifferTimeout c.mu.RUnlock() // Add all URLs found by sniffing c.connsMu.RLock() for _, conn := range c.conns { if !conn.IsDead() { url := conn.URL() if _, found := urlsMap[url]; !found { urls = append(urls, url) } } } c.connsMu.RUnlock() if len(urls) == 0 { return ErrNoClient } // Start sniffing on all found URLs ch := make(chan []*conn, len(urls)) for _, url := range urls { go func(url string) { ch <- c.sniffNode(url) }(url) } // Wait for the results to come back, or the process times out. for { select { case conns := <-ch: if len(conns) > 0 { c.updateConns(conns) return nil } case <-time.After(timeout): // We get here if no cluster responds in time return ErrNoClient } } } // reSniffHostAndPort is used to extract hostname and port from a result // from a Nodes Info API (example: "inet[/127.0.0.1:9200]"). var reSniffHostAndPort = regexp.MustCompile(`\/([^:]*):([0-9]+)\]`) // sniffNode sniffs a single node. This method is run as a goroutine // in sniff. If successful, it returns the list of node URLs extracted // from the result of calling Nodes Info API. Otherwise, an empty array // is returned. func (c *Client) sniffNode(url string) []*conn { nodes := make([]*conn, 0) // Call the Nodes Info API at /_nodes/http req, err := NewRequest("GET", url+"/_nodes/http") if err != nil { return nodes } res, err := c.c.Do((*http.Request)(req)) if err != nil { return nodes } if res == nil { return nodes } if res.Body != nil { defer res.Body.Close() } var info NodesInfoResponse if err := json.NewDecoder(res.Body).Decode(&info); err == nil { if len(info.Nodes) > 0 { switch c.scheme { case "https": for nodeID, node := range info.Nodes { m := reSniffHostAndPort.FindStringSubmatch(node.HTTPSAddress) if len(m) == 3 { url := fmt.Sprintf("https://%s:%s", m[1], m[2]) nodes = append(nodes, newConn(nodeID, url)) } } default: for nodeID, node := range info.Nodes { m := reSniffHostAndPort.FindStringSubmatch(node.HTTPAddress) if len(m) == 3 { url := fmt.Sprintf("http://%s:%s", m[1], m[2]) nodes = append(nodes, newConn(nodeID, url)) } } } } } return nodes } // updateConns updates the clients' connections with new information // gather by a sniff operation. func (c *Client) updateConns(conns []*conn) { c.connsMu.Lock() newConns := make([]*conn, 0) // Build up new connections: // If we find an existing connection, use that (including no. of failures etc.). // If we find a new connection, add it. for _, conn := range conns { var found bool for _, oldConn := range c.conns { if oldConn.NodeID() == conn.NodeID() { // Take over the old connection newConns = append(newConns, oldConn) found = true break } } if !found { // New connection didn't exist, so add it to our list of new conns. c.errorf("elastic: %s joined the cluster", conn.URL()) newConns = append(newConns, conn) } } c.conns = newConns c.cindex = -1 c.connsMu.Unlock() } // healthchecker periodically runs healthcheck. func (c *Client) healthchecker() { for { c.mu.RLock() ticker := time.NewTicker(c.healthcheckInterval) c.mu.RUnlock() select { case <-c.healthcheckStop: // we are asked to stop, so we signal back that we're stopping now c.healthcheckStop <- true return case <-ticker.C: c.healthcheck() } } } // healthcheck does a health check on all nodes in the cluster. Depending on // the node state, it marks connections as dead, sets them alive etc. // If healthchecks are disabled, this is a no-op. func (c *Client) healthcheck() { c.mu.RLock() if !c.healthcheckEnabled { c.mu.RUnlock() return } c.mu.RUnlock() c.connsMu.RLock() conns := c.conns c.connsMu.RUnlock() for _, conn := range conns { params := make(url.Values) params.Set("timeout", "1") req, err := NewRequest("HEAD", conn.URL()+"/?"+params.Encode()) if err == nil { res, err := c.c.Do((*http.Request)(req)) if err == nil { if res.Body != nil { defer res.Body.Close() } if res.StatusCode >= 200 && res.StatusCode < 300 { conn.MarkAsAlive() } else { conn.MarkAsDead() c.errorf("elastic: %s is dead [status=%d]", conn.URL(), res.StatusCode) } } else { c.errorf("elastic: %s is dead", conn.URL()) conn.MarkAsDead() } } else { c.errorf("elastic: %s is dead", conn.URL()) conn.MarkAsDead() } } } // next returns the next available connection, or ErrNoClient. func (c *Client) next() (*conn, error) { // We do round-robin here. // TODO: This should be a pluggable strategy, like the Selector in the official clients. c.connsMu.Lock() defer c.connsMu.Unlock() i := 0 numConns := len(c.conns) for { i += 1 if i > numConns { break // we visited all conns: they all seem to be dead } c.cindex += 1 if c.cindex >= numConns { c.cindex = 0 } conn := c.conns[c.cindex] if !conn.IsDead() { return conn, nil } } // TODO: As a last resort, we could try to awake a dead connection here. // We tried hard, but there is no node available return nil, ErrNoClient } // PerformRequest does a HTTP request to Elasticsearch. // It returns a response and an error on failure. func (c *Client) PerformRequest(method, path string, params url.Values, body interface{}) (*Response, error) { start := time.Now().UTC() c.mu.RLock() retries := c.maxRetries c.mu.RUnlock() var err error var conn *conn var req *Request var resp *Response var retried bool // We wait between retries, using simple exponential back-off. // TODO: Make this configurable, including the jitter. retryWaitMsec := int64(100 + (rand.Intn(20) - 10)) for { pathWithParams := path if len(params) > 0 { pathWithParams += "?" + params.Encode() } // Get a connection conn, err = c.next() if err == ErrNoClient { if !retried { // Force a healtcheck as all connections seem to be dead. c.healthcheck() } retries -= 1 if retries <= 0 { return nil, err } retried = true time.Sleep(time.Duration(retryWaitMsec) * time.Millisecond) retryWaitMsec += retryWaitMsec continue // try again } if err != nil { c.errorf("elastic: cannot get connection from pool") return nil, err } req, err = NewRequest(method, conn.URL()+pathWithParams) if err != nil { c.errorf("elastic: cannot create request for %s %s: %v", strings.ToUpper(method), conn.URL()+pathWithParams, err) return nil, err } // Set body if body != nil { switch b := body.(type) { case string: req.SetBodyString(b) break default: req.SetBodyJson(body) break } } // Tracing c.dumpRequest((*http.Request)(req)) // Get response res, err := c.c.Do((*http.Request)(req)) if err != nil { retries -= 1 if retries <= 0 { c.errorf("elastic: %s is dead", conn.URL()) conn.MarkAsDead() return nil, err } retried = true time.Sleep(time.Duration(retryWaitMsec) * time.Millisecond) retryWaitMsec += retryWaitMsec continue // try again } if res.Body != nil { defer res.Body.Close() } // Check for errors if err := checkResponse(res); err != nil { retries -= 1 if retries <= 0 { return nil, err } retried = true time.Sleep(time.Duration(retryWaitMsec) * time.Millisecond) retryWaitMsec += retryWaitMsec continue // try again } // Tracing c.dumpResponse(res) // We successfully made a request with this connection conn.MarkAsHealthy() resp, err = c.newResponse(res) if err != nil { return nil, err } break } duration := time.Now().UTC().Sub(start) c.infof("%s %s [status:%d, request:%.3fs]", strings.ToUpper(method), req.URL, resp.StatusCode, float64(int64(duration/time.Millisecond))/1000) return resp, nil } // ElasticsearchVersion returns the version number of Elasticsearch // running on the given URL. func (c *Client) ElasticsearchVersion(url string) (string, error) { res, _, err := c.Ping().URL(url).Do() if err != nil { return "", err } return res.Version.Number, nil } // IndexNames returns the names of all indices in the cluster. func (c *Client) IndexNames() ([]string, error) { res, err := c.IndexGetSettings().Index("_all").Do() if err != nil { return nil, err } var names []string for name, _ := range res { names = append(names, name) } return names, nil } // Ping checks if a given node in a cluster exists and (optionally) // returns some basic information about the Elasticsearch server, // e.g. the Elasticsearch version number. func (c *Client) Ping() *PingService { return NewPingService(c) } // CreateIndex returns a service to create a new index. func (c *Client) CreateIndex(name string) *CreateIndexService { builder := NewCreateIndexService(c) builder.Index(name) return builder } // DeleteIndex returns a service to delete an index. func (c *Client) DeleteIndex(name string) *DeleteIndexService { builder := NewDeleteIndexService(c) builder.Index(name) return builder } // IndexExists allows to check if an index exists. func (c *Client) IndexExists(name string) *IndexExistsService { builder := NewIndexExistsService(c) builder.Index(name) return builder } // TypeExists allows to check if one or more types exist in one or more indices. func (c *Client) TypeExists() *IndicesExistsTypeService { return NewIndicesExistsTypeService(c) } // IndexStats provides statistics on different operations happining // in one or more indices. func (c *Client) IndexStats(indices ...string) *IndicesStatsService { builder := NewIndicesStatsService(c) builder = builder.Index(indices...) return builder } // OpenIndex opens an index. func (c *Client) OpenIndex(name string) *OpenIndexService { builder := NewOpenIndexService(c) builder.Index(name) return builder } // CloseIndex closes an index. func (c *Client) CloseIndex(name string) *CloseIndexService { builder := NewCloseIndexService(c) builder.Index(name) return builder } // Index a document. func (c *Client) Index() *IndexService { builder := NewIndexService(c) return builder } // IndexGet retrieves information about one or more indices. // IndexGet is only available for Elasticsearch 1.4 or later. func (c *Client) IndexGet() *IndicesGetService { builder := NewIndicesGetService(c) return builder } // IndexGetSettings retrieves settings about one or more indices. func (c *Client) IndexGetSettings() *IndicesGetSettingsService { builder := NewIndicesGetSettingsService(c) return builder } // Update a document. func (c *Client) Update() *UpdateService { builder := NewUpdateService(c) return builder } // Delete a document. func (c *Client) Delete() *DeleteService { builder := NewDeleteService(c) return builder } // DeleteByQuery deletes documents as found by a query. func (c *Client) DeleteByQuery() *DeleteByQueryService { builder := NewDeleteByQueryService(c) return builder } // Get a document. func (c *Client) Get() *GetService { builder := NewGetService(c) return builder } // MultiGet retrieves multiple documents in one roundtrip. func (c *Client) MultiGet() *MultiGetService { builder := NewMultiGetService(c) return builder } // Exists checks if a document exists. func (c *Client) Exists() *ExistsService { builder := NewExistsService(c) return builder } // Count documents. func (c *Client) Count(indices ...string) *CountService { builder := NewCountService(c) builder.Indices(indices...) return builder } // Search is the entry point for searches. func (c *Client) Search(indices ...string) *SearchService { builder := NewSearchService(c) builder.Indices(indices...) return builder } // Percolate allows to send a document and return matching queries. // See http://www.elastic.co/guide/en/elasticsearch/reference/current/search-percolate.html. func (c *Client) Percolate() *PercolateService { builder := NewPercolateService(c) return builder } // MultiSearch is the entry point for multi searches. func (c *Client) MultiSearch() *MultiSearchService { return NewMultiSearchService(c) } // Suggest returns a service to return suggestions. func (c *Client) Suggest(indices ...string) *SuggestService { builder := NewSuggestService(c) builder.Indices(indices...) return builder } // Scan through documents. Use this to iterate inside a server process // where the results will be processed without returning them to a client. func (c *Client) Scan(indices ...string) *ScanService { builder := NewScanService(c) builder.Indices(indices...) return builder } // Scroll through documents. Use this to efficiently scroll through results // while returning the results to a client. Use Scan when you don't need // to return requests to a client (i.e. not paginating via request/response). func (c *Client) Scroll(indices ...string) *ScrollService { builder := NewScrollService(c) builder.Indices(indices...) return builder } // ClearScroll can be used to clear search contexts manually. func (c *Client) ClearScroll() *ClearScrollService { builder := NewClearScrollService(c) return builder } // Optimize asks Elasticsearch to optimize one or more indices. func (c *Client) Optimize(indices ...string) *OptimizeService { builder := NewOptimizeService(c) builder.Indices(indices...) return builder } // Refresh asks Elasticsearch to refresh one or more indices. func (c *Client) Refresh(indices ...string) *RefreshService { builder := NewRefreshService(c) builder.Indices(indices...) return builder } // Flush asks Elasticsearch to free memory from the index and // flush data to disk. func (c *Client) Flush() *FlushService { builder := NewFlushService(c) return builder } // Explain computes a score explanation for a query and a specific document. func (c *Client) Explain(index, typ, id string) *ExplainService { builder := NewExplainService(c) builder = builder.Index(index).Type(typ).Id(id) return builder } // Bulk is the entry point to mass insert/update/delete documents. func (c *Client) Bulk() *BulkService { builder := NewBulkService(c) return builder } // Alias enables the caller to add and/or remove aliases. func (c *Client) Alias() *AliasService { builder := NewAliasService(c) return builder } // Aliases returns aliases by index name(s). func (c *Client) Aliases() *AliasesService { builder := NewAliasesService(c) return builder } // GetTemplate gets a search template. // Use IndexXXXTemplate funcs to manage index templates. func (c *Client) GetTemplate() *GetTemplateService { return NewGetTemplateService(c) } // PutTemplate creates or updates a search template. // Use IndexXXXTemplate funcs to manage index templates. func (c *Client) PutTemplate() *PutTemplateService { return NewPutTemplateService(c) } // DeleteTemplate deletes a search template. // Use IndexXXXTemplate funcs to manage index templates. func (c *Client) DeleteTemplate() *DeleteTemplateService { return NewDeleteTemplateService(c) } // IndexGetTemplate gets an index template. // Use XXXTemplate funcs to manage search templates. func (c *Client) IndexGetTemplate(names ...string) *IndicesGetTemplateService { builder := NewIndicesGetTemplateService(c) builder = builder.Name(names...) return builder } // IndexTemplateExists gets check if an index template exists. // Use XXXTemplate funcs to manage search templates. func (c *Client) IndexTemplateExists(name string) *IndicesExistsTemplateService { builder := NewIndicesExistsTemplateService(c) builder = builder.Name(name) return builder } // IndexPutTemplate creates or updates an index template. // Use XXXTemplate funcs to manage search templates. func (c *Client) IndexPutTemplate(name string) *IndicesPutTemplateService { builder := NewIndicesPutTemplateService(c) builder = builder.Name(name) return builder } // IndexDeleteTemplate deletes an index template. // Use XXXTemplate funcs to manage search templates. func (c *Client) IndexDeleteTemplate(name string) *IndicesDeleteTemplateService { builder := NewIndicesDeleteTemplateService(c) builder = builder.Name(name) return builder } // GetMapping gets a mapping. func (c *Client) GetMapping() *GetMappingService { return NewGetMappingService(c) } // PutMapping registers a mapping. func (c *Client) PutMapping() *PutMappingService { return NewPutMappingService(c) } // DeleteMapping deletes a mapping. func (c *Client) DeleteMapping() *DeleteMappingService { return NewDeleteMappingService(c) } // ClusterHealth retrieves the health of the cluster. func (c *Client) ClusterHealth() *ClusterHealthService { return NewClusterHealthService(c) } // ClusterState retrieves the state of the cluster. func (c *Client) ClusterState() *ClusterStateService { return NewClusterStateService(c) } // NodesInfo retrieves one or more or all of the cluster nodes information. func (c *Client) NodesInfo() *NodesInfoService { return NewNodesInfoService(c) } // Reindex returns a service that will reindex documents from a source // index into a target index. See // http://www.elastic.co/guide/en/elasticsearch/guide/current/reindex.html // for more information about reindexing. func (c *Client) Reindex(sourceIndex, targetIndex string) *Reindexer { return NewReindexer(c, sourceIndex, targetIndex) }