go-elasticsearch是ES官方提供的Go语言客户端。本文将结合ES 7.12对这个库做一个基本入门使用的演示。
1.开发环境准备
创建一个空的项目,并使用go moudles引入go-elasticsearch的依赖:
mkdir go-es-showcase
cd go-es-showcase
go mod init go-es-showcase
export GOPROXY="https://goproxy.io"
go get github.com/elastic/go-elasticsearch/[email protected]
go get github.com/stretchr/testify这里基于go testing单元测试编码风格来演示对go-elasticsearch这个库的使用。
首先在项目的根目录下创建一个名称为go_esclient_test.go,并演示一下如何创建客户端:
package main
import (
"testing"
es "github.com/elastic/go-elasticsearch/v7"
"github.com/stretchr/testify/assert"
)
var (
client *es.Client
)
func init() {
var err error
client, err = es.NewClient(es.Config{
Addresses: []string{"http://localhost:9200"},
Username: "username",
Password: "password",
})
if err != nil {
log.Fatal(err)
}
}
func TestNewESClient(t *testing.T) {
t.Log(client.Info())
}2.索引的创建、修改和删除
创建索引:
func TestCreateIndex(t *testing.T) {
a := assert.New(t)
response, err := client.Indices.Create("book-0.1.0", client.Indices.Create.WithBody(strings.NewReader(`
{
"aliases": {
"book":{}
},
"settings": {
"analysis": {
"normalizer": {
"lowercase": {
"type": "custom",
"char_filter": [],
"filter": ["lowercase"]
}
}
}
},
"mappings": {
"properties": {
"name": {
"type": "keyword",
"normalizer": "lowercase"
},
"price": {
"type": "double"
},
"summary": {
"type": "text",
"analyzer": "ik_max_word"
},
"author": {
"type": "keyword"
},
"pubDate": {
"type": "date"
},
"pages": {
"type": "integer"
}
}
}
}
`)))
a.Nil(err)
t.Log(response)
}查看索引信息:
func TestGetIndex(t *testing.T) {
a := assert.New(t)
response, err := client.Indices.Get([]string{"book"})
a.Nil(err)
t.Log(response)
}删除索引:
func TestDeleteIndex(t *testing.T) {
a := assert.New(t)
response, err := client.Indices.Delete([]string{"book-0.1.0"})
a.Nil(err)
t.Log(response)
}3.文档的创建、修改和删除
创建文档:
type Book struct {
Author string `json:"author"`
Name string `json:"name"`
Pages int `json:"pages"`
Price float64 `json:"price"`
PubDate *time.Time `json:"pubDate"`
Summary string `json:"summary"`
}
func TestCreateDocument(t *testing.T) {
a := assert.New(t)
body := &bytes.Buffer{}
pubDate := time.Now()
err := json.NewEncoder(body).Encode(&Book{
Author: "金庸",
Price: 96.0,
Name: "天龙八部",
Pages: 1978,
PubDate: &pubDate,
Summary: "...",
})
a.Nil(err)
response, err := client.Create("book", "10001", body)
a.Nil(err)
t.Log(response)
}覆盖性更新文档,如果给定的文档ID不存在,将创建文档:
func TestIndexDocument(t *testing.T) {
a := assert.New(t)
body := &bytes.Buffer{}
pubDate := time.Now()
err := json.NewEncoder(body).Encode(&Book{
Author: "金庸",
Price: 96.0,
Name: "天龙八部",
Pages: 1978,
PubDate: &pubDate,
Summary: "...",
})
a.Nil(err)
response, err := client.Index("book", body, client.Index.WithDocumentID("10001"))
a.Nil(err)
t.Log(response)
}局部性更新文档,下面的代码借助go json的omitempty,在将更新数据对象序列化成json,可以只序列化非零值字段,实现局部更新。
实际项目采用这种方式时,需要注意某个字段的零值具有业务意义时,可以采用对应的指针类型实现。
type doc struct {
Doc interface{} `json:"doc"`
}
type Book struct {
Author string `json:"author,omitempty"`
Name string `json:"name,omitempty"`
Pages int `json:"pages,omitempty"`
Price float64 `json:"price,omitempty"`
PubDate *time.Time `json:"pubDate,omitempty"`
Summary string `json:"summary,omitempty"`
}
func TestPartialUpdateDocument(t *testing.T) {
a := assert.New(t)
body := &bytes.Buffer{}
err := json.NewEncoder(body).Encode(&doc{
Doc: &Book{
Name: "《天龙八部》",
},
})
a.Nil(err)
response, err := client.Update("book", "10001", body)
a.Nil(err)
t.Log(response)
}删除文档:
func TestDeleteDocument(t *testing.T) {
a := assert.New(t)
response, err := client.Delete("book", "10001")
a.Nil(err)
t.Log(response)
}获取文档:
func TestGetDocument(t *testing.T) {
a := assert.New(t)
response, err := client.Get("book", "10001")
a.Nil(err)
t.Log(response)
}4.文档的批量创建和删除
批量操作对应ES的REST API是:
POST /<target>/_bulk
{ "index" : { "_id" : "1" } }
{ "field1" : "value1" }
{ "delete" : { "_id" : "2" } }
{ "create" : { "_id" : "3" } }
{ "field1" : "value3" }
{ "update" : {"_id" : "1" } }
{ "doc" : {"field2" : "value2"} }对应index, create, update操作,提交的数据都是由两行组成,第一行是meta数据,描述操作信息,第二行是具体提交的数据,对于delete操作只有一行meta数据。
对照REST API,在go client的示例代码如下:
func TestBulk(t *testing.T) {
createBooks := []*Book{
{
ID: "10002",
Name: "神雕侠侣",
Author: "金庸",
},
{
ID: "10003",
Name: "射雕英雄传",
Author: "金庸",
},
}
deleteBookIds := []string{"10001"}
a := assert.New(t)
body := &bytes.Buffer{}
for _, book := range createBooks {
meta := []byte(fmt.Sprintf(`{ "index" : { "_id" : "%s" } }%s`, book.ID, "\n"))
data, err := json.Marshal(book)
a.Nil(err)
data = append(data, "\n"...)
body.Grow(len(meta) + len(data))
body.Write(meta)
body.Write(data)
}
for _, id := range deleteBookIds {
meta := []byte(fmt.Sprintf(`{ "delete" : { "_id" : "%s" } }%s`, id, "\n"))
body.Grow(len(meta))
body.Write(meta)
}
t.Log(body.String())
response, err := client.Bulk(body, client.Bulk.WithIndex("book"))
a.Nil(err)
t.Log(response)
}5.文档搜索
下面代码演示了go-elasticsearch提供的搜索查询功能,实际中查询请求体建议使用go template动态生成。
func TestSearch(t *testing.T) {
a := assert.New(t)
body := &bytes.Buffer{}
body.WriteString(`
{
"_source":{
"excludes": ["author"]
},
"query": {
"match_phrase": {
"author": "古龙"
}
},
"sort": [
{
"pages": {
"order": "desc"
}
}
],
"from": 0,
"size": 5
}
`)
response, err := client.Search(client.Search.WithIndex("book"), client.Search.WithBody(body))
a.Nil(err)
t.Log(response)
}