An Elasticsearch client for the Go programming language.
Elastic is a client for Elasticsearch for the Go programming language. We use it in production since 2012. It supports Elasticsearch versions 1.x, 2.x, 5.x, 6.x and 7.x.
The code is MIT licensed and hosted on GitHub. Please report issues on GitHub. If you want to have a deeper look and are searching for more examples, the wiki on GitHub is a good place to go.
Elastic supports different versions of Elasticsearch. However, you must choose the version of Elastic that matches the Elasticsearch version.
Elasticsearch version | Elastic version | Package URL |
---|---|---|
7.x | 7.0 | https://github.com/olivere/elastic/v7 (source • docs • changelog) |
6.x | 6.0 | https://github.com/olivere/elastic (source • docs • changelog) |
5.x | 5.0 | https://gopkg.in/olivere/elastic.v5 (source • docs • changelog) |
2.x | 3.0 | https://gopkg.in/olivere/elastic.v3 (source • docs • changelog) |
1.x | 2.0 | https://gopkg.in/olivere/elastic.v2 (source • docs) |
0.9-1.3 | 1.0 | https://gopkg.in/olivere/elastic.v1 (source • docs) |
Example: You have Elasticsearch 5.0.0 installed and want to use Elastic. As listed above, you should use Elastic 5.0. So you first install Elastic 5.0 with:
go get gopkg.in/olivere/elastic.v5
You the use the following import path in your Go code:
import "gopkg.in/olivere/elastic.v5"
The GitHub repository at https://github.com/olivere/elastic always refers to the latest version of Elastic. If you want to use stable versions of Elastic, you should use a tagged version.
Starting with Elastic 7.0, we support Go modules. You must use an import path of
github.com/olivere/elastic/v7
. Notice that you also must use a
version of Go that is compatible with Go modules (Go 1.10.3 or later).
With Elastic 6.0 we started tagging releases directly on
github.com/olivere/elastic
. So with Elastic 6.x forward, you
should also be able to use
dep. Notice however that dep still
lacks support for version numbers in import paths (see
PR #1963).
Earlier versions of Elastic (5.x and earlier) published packages via gopkg.in as described above.
The following code targets Elasticsearch 6.x and uses Elastic 6.0.
To use Elastic 6.0 in your project, first go get
it:
go get github.com/olivere/elastic
Then start using it in your code. Here's a complete example:
package main
import (
"context"
"encoding/json"
"fmt"
"reflect"
"time"
"github.com/olivere/elastic"
)
// Tweet is a structure used for serializing/deserializing data in Elasticsearch.
type Tweet struct {
User string `json:"user"`
Message string `json:"message"`
Retweets int `json:"retweets"`
Image string `json:"image,omitempty"`
Created time.Time `json:"created,omitempty"`
Tags []string `json:"tags,omitempty"`
Location string `json:"location,omitempty"`
Suggest *elastic.SuggestField `json:"suggest_field,omitempty"`
}
const mapping = `
{
"settings":{
"number_of_shards": 1,
"number_of_replicas": 0
},
"mappings":{
"tweet":{
"properties":{
"user":{
"type":"keyword"
},
"message":{
"type":"text",
"store": true,
"fielddata": true
},
"image":{
"type":"keyword"
},
"created":{
"type":"date"
},
"tags":{
"type":"keyword"
},
"location":{
"type":"geo_point"
},
"suggest_field":{
"type":"completion"
}
}
}
}
}`
func main() {
// Starting with elastic.v5, you must pass a context to execute each service
ctx := context.Background()
// Obtain a client and connect to the default Elasticsearch installation
// on 127.0.0.1:9200. Of course you can configure your client to connect
// to other hosts and configure it in various other ways.
client, err := elastic.NewClient()
if err != nil {
// Handle error
panic(err)
}
// Ping the Elasticsearch server to get e.g. the version number
info, code, err := client.Ping("http://127.0.0.1:9200").Do(ctx)
if err != nil {
// Handle error
panic(err)
}
fmt.Printf("Elasticsearch returned with code %d and version %s\n", code, info.Version.Number)
// Getting the ES version number is quite common, so there's a shortcut
esversion, err := client.ElasticsearchVersion("http://127.0.0.1:9200")
if err != nil {
// Handle error
panic(err)
}
fmt.Printf("Elasticsearch version %s\n", esversion)
// Use the IndexExists service to check if a specified index exists.
exists, err := client.IndexExists("twitter").Do(ctx)
if err != nil {
// Handle error
panic(err)
}
if !exists {
// Create a new index.
createIndex, err := client.CreateIndex("twitter").BodyString(mapping).Do(ctx)
if err != nil {
// Handle error
panic(err)
}
if !createIndex.Acknowledged {
// Not acknowledged
}
}
// Index a tweet (using JSON serialization)
tweet1 := Tweet{User: "olivere", Message: "Take Five", Retweets: 0}
put1, err := client.Index().
Index("twitter").
Type("tweet").
Id("1").
BodyJson(tweet1).
Do(ctx)
if err != nil {
// Handle error
panic(err)
}
fmt.Printf("Indexed tweet %s to index %s, type %s\n", put1.Id, put1.Index, put1.Type)
// Index a second tweet (by string)
tweet2 := `{"user" : "olivere", "message" : "It's a Raggy Waltz"}`
put2, err := client.Index().
Index("twitter").
Type("tweet").
Id("2").
BodyString(tweet2).
Do(ctx)
if err != nil {
// Handle error
panic(err)
}
fmt.Printf("Indexed tweet %s to index %s, type %s\n", put2.Id, put2.Index, put2.Type)
// Get tweet with specified ID
get1, err := client.Get().
Index("twitter").
Type("tweet").
Id("1").
Do(ctx)
if err != nil {
// Handle error
panic(err)
}
if get1.Found {
fmt.Printf("Got document %s in version %d from index %s, type %s\n", get1.Id, get1.Version, get1.Index, get1.Type)
}
// Flush to make sure the documents got written.
_, err = client.Flush().Index("twitter").Do(ctx)
if err != nil {
panic(err)
}
// Search with a term query
termQuery := elastic.NewTermQuery("user", "olivere")
searchResult, err := client.Search().
Index("twitter"). // search in index "twitter"
Query(termQuery). // specify the query
Sort("user", true). // sort by "user" field, ascending
From(0).Size(10). // take documents 0-9
Pretty(true). // pretty print request and response JSON
Do(ctx) // execute
if err != nil {
// Handle error
panic(err)
}
// searchResult is of type SearchResult and returns hits, suggestions,
// and all kinds of other information from Elasticsearch.
fmt.Printf("Query took %d milliseconds\n", searchResult.TookInMillis)
// Each is a convenience function that iterates over hits in a search result.
// It makes sure you don't need to check for nil values in the response.
// However, it ignores errors in serialization. If you want full control
// over iterating the hits, see below.
var ttyp Tweet
for _, item := range searchResult.Each(reflect.TypeOf(ttyp)) {
if t, ok := item.(Tweet); ok {
fmt.Printf("Tweet by %s: %s\n", t.User, t.Message)
}
}
// TotalHits is another convenience function that works even when something goes wrong.
fmt.Printf("Found a total of %d tweets\n", searchResult.TotalHits())
// Here's how you iterate through results with full control over each step.
if searchResult.Hits.TotalHits > 0 {
fmt.Printf("Found a total of %d tweets\n", searchResult.Hits.TotalHits)
// Iterate through results
for _, hit := range searchResult.Hits.Hits {
// hit.Index contains the name of the index
// Deserialize hit.Source into a Tweet (could also be just a map[string]interface{}).
var t Tweet
err := json.Unmarshal(*hit.Source, &t)
if err != nil {
// Deserialization failed
}
// Work with tweet
fmt.Printf("Tweet by %s: %s\n", t.User, t.Message)
}
} else {
// No hits
fmt.Print("Found no tweets\n")
}
// Update a tweet by the update API of Elasticsearch.
// We just increment the number of retweets.
update, err := client.Update().Index("twitter").Type("tweet").Id("1").
Script(elastic.NewScriptInline("ctx._source.retweets += params.num").Lang("painless").Param("num", 1)).
Upsert(map[string]interface{}{"retweets": 0}).
Do(ctx)
if err != nil {
// Handle error
panic(err)
}
fmt.Printf("New version of tweet %q is now %d\n", update.Id, update.Version)
// ...
// Delete an index.
deleteIndex, err := client.DeleteIndex("twitter").Do(ctx)
if err != nil {
// Handle error
panic(err)
}
if !deleteIndex.Acknowledged {
// Not acknowledged
}
}