go-elasticsearch
The official Go client for Elasticsearch.
Compatibility
Language clients are forward compatible; meaning that clients support communicating with greater or equal minor versions of Elasticsearch.
Elasticsearch language clients are only backwards compatible with default distributions and without guarantees made.
When using Go modules, include the version in the import path, and specify either an explicit version or a branch:
require github.com/elastic/go-elasticsearch/v7 7.16
require github.com/elastic/go-elasticsearch/v7 7.0.0
It's possible to use multiple versions of the client in a single project:
// go.mod
github.com/elastic/go-elasticsearch/v6 6.x
github.com/elastic/go-elasticsearch/v7 7.16
// main.go
import (
elasticsearch6 "github.com/elastic/go-elasticsearch/v6"
elasticsearch7 "github.com/elastic/go-elasticsearch/v7"
)
// ...
es6, _ := elasticsearch6.NewDefaultClient()
es7, _ := elasticsearch7.NewDefaultClient()
The main
branch of the client is compatible with the current master
branch of Elasticsearch.
Installation
Add the package to your go.mod
file:
require github.com/elastic/go-elasticsearch/v8 main
Or, clone the repository:
git clone --branch main https://github.com/elastic/go-elasticsearch.git $GOPATH/src/github.com/elastic/go-elasticsearch
A complete example:
mkdir my-elasticsearch-app && cd my-elasticsearch-app
cat > go.mod <<-END
module my-elasticsearch-app
require github.com/elastic/go-elasticsearch/v8 main
END
cat > main.go <<-END
package main
import (
"log"
"github.com/elastic/go-elasticsearch/v7"
)
func main() {
es, _ := elasticsearch.NewDefaultClient()
log.Println(elasticsearch.Version)
log.Println(es.Info())
}
END
go run main.go
Usage
The elasticsearch
package ties together two separate packages for calling the Elasticsearch APIs and transferring data over HTTP: esapi
and estransport
, respectively.
Use the elasticsearch.NewDefaultClient()
function to create the client with the default settings.
es, err := elasticsearch.NewDefaultClient()
if err != nil {
log.Fatalf("Error creating the client: %s", err)
}
res, err := es.Info()
if err != nil {
log.Fatalf("Error getting response: %s", err)
}
defer res.Body.Close()
log.Println(res)
NOTE: It is critical to both close the response body and to consume it, in order to re-use persistent TCP connections in the default HTTP transport. If you're not interested in the response body, call io.Copy(ioutil.Discard, res.Body)
.
When you export the ELASTICSEARCH_URL
environment variable,
it will be used to set the cluster endpoint(s). Separate multiple adresses by a comma.
To set the cluster endpoint(s) programatically, pass a configuration object
to the elasticsearch.NewClient()
function.
cfg := elasticsearch.Config{
Addresses: []string{
"http://localhost:9200",
"http://localhost:9201",
},
}
es, err := elasticsearch.NewClient(cfg)
To set the username and password, include them in the endpoint URL,
or use the corresponding configuration options.
cfg := elasticsearch.Config{
Username: "foo",
Password: "bar",
}
To set a custom certificate authority used to sign the certificates of cluster nodes,
use the CACert
configuration option.
cert, _ := ioutil.ReadFile(*cacert)
cfg := elasticsearch.Config{
CACert: cert,
}
To configure other HTTP settings, pass an http.Transport
object in the configuration object.
cfg := elasticsearch.Config{
Transport: &http.Transport{
MaxIdleConnsPerHost: 10,
ResponseHeaderTimeout: time.Second,
TLSClientConfig: &tls.Config{
MinVersion: tls.VersionTLS12,
},
},
}
See the _examples/configuration.go
and
_examples/customization.go
files for
more examples of configuration and customization of the client.
See the _examples/security
for an example of a security configuration.
The following example demonstrates a more complex usage. It fetches the Elasticsearch version from the cluster, indexes a couple of documents concurrently, and prints the search results, using a lightweight wrapper around the response body.
package main
import (
"bytes"
"context"
"encoding/json"
"log"
"strconv"
"strings"
"sync"
"github.com/elastic/go-elasticsearch/v7"
"github.com/elastic/go-elasticsearch/v7/esapi"
)
func main() {
log.SetFlags(0)
var (
r map[string]interface{}
wg sync.WaitGroup
)
es, err := elasticsearch.NewDefaultClient()
if err != nil {
log.Fatalf("Error creating the client: %s", err)
}
res, err := es.Info()
if err != nil {
log.Fatalf("Error getting response: %s", err)
}
defer res.Body.Close()
if res.IsError() {
log.Fatalf("Error: %s", res.String())
}
if err := json.NewDecoder(res.Body).Decode(&r); err != nil {
log.Fatalf("Error parsing the response body: %s", err)
}
log.Printf("Client: %s", elasticsearch.Version)
log.Printf("Server: %s", r["version"].(map[string]interface{})["number"])
log.Println(strings.Repeat("~", 37))
for i, title := range []string{"Test One", "Test Two"} {
wg.Add(1)
go func(i int, title string) {
defer wg.Done()
var b strings.Builder
b.WriteString(`{"title" : "`)
b.WriteString(title)
b.WriteString(`"}`)
req := esapi.IndexRequest{
Index: "test",
DocumentID: strconv.Itoa(i + 1),
Body: strings.NewReader(b.String()),
Refresh: "true",
}
res, err := req.Do(context.Background(), es)
if err != nil {
log.Fatalf("Error getting response: %s", err)
}
defer res.Body.Close()
if res.IsError() {
log.Printf("[%s] Error indexing document ID=%d", res.Status(), i+1)
} else {
var r map[string]interface{}
if err := json.NewDecoder(res.Body).Decode(&r); err != nil {
log.Printf("Error parsing the response body: %s", err)
} else {
log.Printf("[%s] %s; version=%d", res.Status(), r["result"], int(r["_version"].(float64)))
}
}
}(i, title)
}
wg.Wait()
log.Println(strings.Repeat("-", 37))
var buf bytes.Buffer
query := map[string]interface{}{
"query": map[string]interface{}{
"match": map[string]interface{}{
"title": "test",
},
},
}
if err := json.NewEncoder(&buf).Encode(query); err != nil {
log.Fatalf("Error encoding query: %s", err)
}
res, err = es.Search(
es.Search.WithContext(context.Background()),
es.Search.WithIndex("test"),
es.Search.WithBody(&buf),
es.Search.WithTrackTotalHits(true),
es.Search.WithPretty(),
)
if err != nil {
log.Fatalf("Error getting response: %s", err)
}
defer res.Body.Close()
if res.IsError() {
var e map[string]interface{}
if err := json.NewDecoder(res.Body).Decode(&e); err != nil {
log.Fatalf("Error parsing the response body: %s", err)
} else {
log.Fatalf("[%s] %s: %s",
res.Status(),
e["error"].(map[string]interface{})["type"],
e["error"].(map[string]interface{})["reason"],
)
}
}
if err := json.NewDecoder(res.Body).Decode(&r); err != nil {
log.Fatalf("Error parsing the response body: %s", err)
}
log.Printf(
"[%s] %d hits; took: %dms",
res.Status(),
int(r["hits"].(map[string]interface{})["total"].(map[string]interface{})["value"].(float64)),
int(r["took"].(float64)),
)
for _, hit := range r["hits"].(map[string]interface{})["hits"].([]interface{}) {
log.Printf(" * ID=%s, %s", hit.(map[string]interface{})["_id"], hit.(map[string]interface{})["_source"])
}
log.Println(strings.Repeat("=", 37))
}
As you see in the example above, the esapi
package allows to call the Elasticsearch APIs in two distinct ways: either by creating a struct, such as IndexRequest
, and calling its Do()
method by passing it a context and the client, or by calling the Search()
function on the client directly, using the option functions such as WithIndex()
. See more information and examples in the
package documentation.
The estransport
package handles the transfer of data to and from Elasticsearch, including retrying failed requests, keeping a connection pool, discovering cluster nodes and logging.
Read more about the client internals and usage in the following blog posts:
Helpers
The esutil
package provides convenience helpers for working with the client. At the moment, it provides the esutil.JSONReader()
and the esutil.BulkIndexer
helpers.
Examples
The _examples
folder contains a number of recipes and comprehensive examples to get you started with the client, including configuration and customization of the client, using a custom certificate authority (CA) for security (TLS), mocking the transport for unit tests, embedding the client in a custom type, building queries, performing requests individually and in bulk, and parsing the responses.
License
(c) 2019 Elasticsearch. Licensed under the Apache License, Version 2.0.