文章目录
代码地址:https://gitee.com/lymgoforIT/golang-trick/tree/master/35-go-elasticsearch
一、简介
go-elasticsearch
是
Elasticsearch
官方提供的
Go
客户端。每个
Elasticsearch
版本会有一个对应的
go-elasticsearch
版本。官方会维护最近的两个主要版本。
go-elasticsearch
提供了
Low-level
和
Fully-typed
两套
API
。本文以
Fully-typed API
为例介绍
go-elasticsearch
的常用方法。
本文接下来将以电商平台 “用户评价” 数据为例,演示
Go
语言
Elasticsearch
客户端的相关操作。原文参考地址:https://mp.weixin.qq.com/s/Em-xPi2ZqBALiFX9ebb2fw
关于如何使用 docker 在本地搭建 Elasticsearch 环境请查看我之前的博客: 56.windows docker 安装ES、Go操作ES(github.com/olivere/elastic/v7库)
1、安装依赖
执行以下命令安装
v8
版本的
go
客户端。
go get github.com/elastic/go-elasticsearch/v8@latest
2、导入依赖
import"github.com/elastic/go-elasticsearch/v8"
可以根据实际需求导入不同的客户端版本,也支持在一个项目中导入不同的客户端版本。
import(
elasticsearch7 "github.com/elastic/go-elasticsearch/v7"
elasticsearch8 "github.com/elastic/go-elasticsearch/v8")// ...
es7,_:= elasticsearch7.NewDefaultClient()
es8,_:= elasticsearch8.NewDefaultClient()
3、连接 ES
指定要连接
ES
的相关配置,并创建客户端连接。
这里按实际工作中的习惯创建目录。如在项目下创建
dao
目录,其下面可能继续有
MySQL、Redis、MQ、ES
等目录,本文只涉及到
ES
,所以就是先建
es
目录,接着下面建立
base.go
用于创建连接和初始化等工作。之后的索引和文档等操作可以分别新建对应的
.go
文件,当然,操作
es
的函数很多时,也可以根据业务拆分建立
.go
文件。
base.go
package es
import("fmt"
elasticsearch "github.com/elastic/go-elasticsearch/v8")var EsClinet *elasticsearch.TypedClient
funcInitEsConn(){// ES 配置
cfg := elasticsearch.Config{
Addresses:[]string{"http://localhost:9200",},}// 创建客户端连接
client, err := elasticsearch.NewTypedClient(cfg)if err !=nil{
fmt.Printf("elasticsearch.NewTypedClient failed, err:%v\n", err)panic(err)}
EsClinet = client
}
main.go
package main
import"golang-trick/35-go-elasticsearch/dao/es"funcmain(){// 初始化ES客户端全局连接
es.InitEsConn()}
二、操作索引
索引主要包含创建和删除操作
索引相关操作我们都放到
index.go
文件中
package es
import("context""fmt")// CreateIndex 创建索引funcCreateIndex(indexName string)error{
resp, err := EsClinet.Indices.Create(indexName).Do(context.Background())if err !=nil{
fmt.Printf("create index failed, err:%v\n", err)return err
}
fmt.Printf("index:%#v\n", resp.Index)returnnil}// DeleteIndex 删除索引funcDeleteIndex(indexName string)error{_, err := EsClinet.Indices.// 表明是对索引的操作,而Index则表示是要操作具体索引下的文档Delete(indexName).Do(context.Background())if err !=nil{
fmt.Printf("delete index failed,err:%v\n", err)return err
}
fmt.Printf("delete index successed,indexName:%s", indexName)returnnil}
创建一个名为
my-review-1
(用户评价) 的
index
。
main.go
package main
import"golang-trick/35-go-elasticsearch/dao/es"funcmain(){// 初始化ES客户端全局连接
es.InitEsConn()
es.CreateIndex("my-review-1")}
三、model定义
本文主要是对用户评价做检索案例,所以需要建立
model
,我们一般习惯在项目路径下建立
model包
专门存放
model
,入下:
定义与
document
数据对应的
Review
(评价数据) 和
Tag
(评价标签) 结构体。
review.go
// Review 评价数据type Review struct{
ID int64`json:"id"`
UserID int64`json:"userID"`
Score uint8`json:"score"`
Content string`json:"content"`
Tags []Tag `json:"tags"`
Status int`json:"status"`
PublishTime time.Time `json:"publishDate"`}// Tag 评价标签type Tag struct{
Code int`json:"code"`
Title string`json:"title"`}
四、操作文档
1、创建文档
创建一条
document
并添加到
my-review-1
的
index
中。
在
es
包下创建
review_doc.go
文件,表示是对于
review
的文档相关操作。
review_doc.go
package es
import("context""fmt""golang-trick/35-go-elasticsearch/model""strconv")// indexDocument 索引文档funcCreateDocument(review model.Review, indexName string){// 添加文档
resp, err := EsClinet.Index(indexName).Id(strconv.FormatInt(review.ID,10)).// 指定文档唯一IDDocument(review).Do(context.Background())if err !=nil{
fmt.Printf("indexing document failed, err:%v\n", err)return}
fmt.Printf("result:%#v\n", resp.Result)}
main.go
package main
import("golang-trick/35-go-elasticsearch/dao/es""golang-trick/35-go-elasticsearch/model""time")funcmain(){// 初始化ES客户端全局连接
es.InitEsConn()// es.CreateIndex("my-review-1")// 定义 document 结构体对象
d1 := model.Review{
ID:1,
UserID:147982601,
Score:5,
Content:"这是一个好评!",
Tags:[]model.Tag{{1000,"好评"},{1100,"物超所值"},{9000,"有图"},},
Status:2,
PublishTime: time.Now(),}
es.CreateDocument(d1,"my-review-1")}
2、根据文档唯一ID获取指定索引下的文档
// GetDocument 根据文档ID获取文档funcGetDocumentByDocId(id string, indexName string){
resp, err := EsClinet.Get(indexName, id).Do(context.Background())if err !=nil{
fmt.Printf("get document by id failed, err:%v\n", err)return}
fmt.Printf("fileds:%s\n", resp.Source_)}
3、检索 document
1、 检索全部文档
构建搜索查询可以使用结构化的查询条件。
// SearchAllDocument 搜索指定索引下所有文档funcSearchAllDocument(indexName string){// 搜索文档
resp, err := EsClinet.Search().Index(indexName).Request(&search.Request{
Query:&types.Query{
MatchAll:&types.MatchAllQuery{},},}).Do(context.Background())if err !=nil{
fmt.Printf("search document failed, err:%v\n", err)return}
fmt.Printf("total: %d\n", resp.Hits.Total.Value)// 遍历所有结果for_, hit :=range resp.Hits.Hits {
fmt.Printf("%s\n", hit.Source_)}}
2、 模糊条件检索
下面是在
my-review-1
中搜索
content
包含 “好评” 的文档。
// SearchDocument 指定条件搜索文档funcSearchDocument(indexName string){// 搜索content中包含好评的文档
resp, err := EsClinet.Search().Index(indexName).Request(&search.Request{
Query:&types.Query{
MatchPhrase:map[string]types.MatchPhraseQuery{"content":{Query:"好评"},},},}).Do(context.Background())if err !=nil{
fmt.Printf("search document failed, err:%v\n", err)return}
fmt.Printf("total: %d\n", resp.Hits.Total.Value)// 遍历所有结果for_, hit :=range resp.Hits.Hits {
fmt.Printf("%s\n", hit.Source_)}}
3、聚合检索
在
my-review-1
上运行一个平均值聚合,得到所有文档
score
的平均值。
// AggregationDemo 聚合funcAggregationDemo(indexName string){
avgScoreAgg, err := EsClinet.Search().Index(indexName).Request(&search.Request{
Size: some.Int(0),
Aggregations:map[string]types.Aggregations{"avg_score":{// 将所有文档的 score 的平均值聚合为 avg_score
Avg:&types.AverageAggregation{
Field: some.String("score"),},},},},).Do(context.Background())if err !=nil{
fmt.Printf("aggregation failed, err:%v\n", err)return}
fmt.Printf("avgScore:%#v\n", avgScoreAgg.Aggregations["avg_score"])}
4、更新文档
使用新值更新文档。
// UpdateDocument 更新文档funcUpdateDocument(review model.Review, indexName string){ 修改后的结构体变量//d1 := Review{// ID: 1,// UserID: 147982601,// Score: 5,// Content: "这是一个修改后的好评!", // 有修改// Tags: []Tag{ // 有修改// {1000, "好评"},// {9000, "有图"},// },// Status: 2,// PublishTime: time.Now(),//}
resp, err := EsClinet.Update(indexName, fmt.Sprintf("%d", review.ID)).// 通过唯一文档ID指定要更新的文档Doc(review).// 使用结构体变量更新Do(context.Background())if err !=nil{
fmt.Printf("update document failed, err:%v\n", err)return}
fmt.Printf("result:%v\n", resp.Result)}
更新可以使用结构体变量也可以使用原始
JSON
字符串数据。
// UpdateDocumentByJson 更新文档funcUpdateDocumentByJson(docId,str ,indexName string){ 修改后的JSON字符串//str := `{// "id":1,// "userID":147982601,// "score":5,// "content":"这是一个二次修改后的好评!",// "tags":[// {// "code":1000,// "title":"好评"// },// {// "code":9000,// "title":"有图"// }// ],// "status":2,// "publishDate":"2023-12-16T15:27:18.219385+08:00"//}`// 直接使用JSON字符串更新
resp, err := EsClinet.Update(indexName, docId).Request(&update.Request{
Doc: json.RawMessage(str),}).Do(context.Background())if err !=nil{
fmt.Printf("update document failed, err:%v\n", err)return}
fmt.Printf("result:%v\n", resp.Result)}
5、删除文档
根据文档
id
删除文档。
// DeleteDocument 删除 documentfuncDeleteDocument(docId,indexName string){
resp, err := EsClinet.Delete(indexName, docId).Do(context.Background())if err !=nil{
fmt.Printf("delete document failed, err:%v\n", err)return}
fmt.Printf("result:%v\n", resp.Result)}
6、文档操作完整代码
package es
import("context""encoding/json""fmt""github.com/elastic/go-elasticsearch/v8/typedapi/core/search""github.com/elastic/go-elasticsearch/v8/typedapi/core/update""github.com/elastic/go-elasticsearch/v8/typedapi/some""github.com/elastic/go-elasticsearch/v8/typedapi/types""golang-trick/35-go-elasticsearch/model""strconv")// indexDocument 索引文档funcCreateDocument(review model.Review, indexName string){// 添加文档
resp, err := EsClinet.Index(indexName).// 表示是要操作具体索引下的文档Id(strconv.FormatInt(review.ID,10)).// 指定文档唯一IDDocument(review).Do(context.Background())if err !=nil{
fmt.Printf("indexing document failed, err:%v\n", err)return}
fmt.Printf("result:%#v\n", resp.Result)}// GetDocument 根据文档ID获取文档funcGetDocumentByDocId(id string, indexName string){
resp, err := EsClinet.Get(indexName, id).Do(context.Background())if err !=nil{
fmt.Printf("get document by id failed, err:%v\n", err)return}
fmt.Printf("fileds:%s\n", resp.Source_)}// SearchAllDocument 搜索指定索引下所有文档funcSearchAllDocument(indexName string){// 搜索文档
resp, err := EsClinet.Search().Index(indexName).Request(&search.Request{
Query:&types.Query{
MatchAll:&types.MatchAllQuery{},},}).Do(context.Background())if err !=nil{
fmt.Printf("search document failed, err:%v\n", err)return}
fmt.Printf("total: %d\n", resp.Hits.Total.Value)// 遍历所有结果for_, hit :=range resp.Hits.Hits {
fmt.Printf("%s\n", hit.Source_)}}// SearchDocument 指定条件搜索文档funcSearchDocument(indexName string){// 搜索content中包含好评的文档
resp, err := EsClinet.Search().Index(indexName).Request(&search.Request{
Query:&types.Query{
MatchPhrase:map[string]types.MatchPhraseQuery{"content":{Query:"好评"},},},}).Do(context.Background())if err !=nil{
fmt.Printf("search document failed, err:%v\n", err)return}
fmt.Printf("total: %d\n", resp.Hits.Total.Value)// 遍历所有结果for_, hit :=range resp.Hits.Hits {
fmt.Printf("%s\n", hit.Source_)}}// AggregationDemo 聚合funcAggregationDemo(indexName string){
avgScoreAgg, err := EsClinet.Search().Index(indexName).Request(&search.Request{
Size: some.Int(0),
Aggregations:map[string]types.Aggregations{"avg_score":{// 将所有文档的 score 的平均值聚合为 avg_score
Avg:&types.AverageAggregation{
Field: some.String("score"),},},},},).Do(context.Background())if err !=nil{
fmt.Printf("aggregation failed, err:%v\n", err)return}
fmt.Printf("avgScore:%#v\n", avgScoreAgg.Aggregations["avg_score"])}// UpdateDocument 更新文档funcUpdateDocument(review model.Review, indexName string){ 修改后的结构体变量//d1 := Review{// ID: 1,// UserID: 147982601,// Score: 5,// Content: "这是一个修改后的好评!", // 有修改// Tags: []Tag{ // 有修改// {1000, "好评"},// {9000, "有图"},// },// Status: 2,// PublishTime: time.Now(),//}
resp, err := EsClinet.Update(indexName, fmt.Sprintf("%d", review.ID)).// 通过唯一文档ID指定要更新的文档Doc(review).// 使用结构体变量更新Do(context.Background())if err !=nil{
fmt.Printf("update document failed, err:%v\n", err)return}
fmt.Printf("result:%v\n", resp.Result)}// UpdateDocumentByJson 更新文档funcUpdateDocumentByJson(docId, str, indexName string){ 修改后的JSON字符串//str := `{// "id":1,// "userID":147982601,// "score":5,// "content":"这是一个二次修改后的好评!",// "tags":[// {// "code":1000,// "title":"好评"// },// {// "code":9000,// "title":"有图"// }// ],// "status":2,// "publishDate":"2023-12-16T15:27:18.219385+08:00"//}`// 直接使用JSON字符串更新
resp, err := EsClinet.Update(indexName, docId).Request(&update.Request{
Doc: json.RawMessage(str),}).Do(context.Background())if err !=nil{
fmt.Printf("update document failed, err:%v\n", err)return}
fmt.Printf("result:%v\n", resp.Result)}// DeleteDocument 删除 documentfuncDeleteDocument(docId, indexName string){
resp, err := EsClinet.Delete(indexName, docId).Do(context.Background())if err !=nil{
fmt.Printf("delete document failed, err:%v\n", err)return}
fmt.Printf("result:%v\n", resp.Result)}
版权归原作者 百里守约学编程 所有, 如有侵权,请联系我们删除。