go-elasticsearch 使用指南

本文是 go-elasticsearch 庫的使用指南。

go-elasticsearch 是 Elasticsearch 官方提供的 Go 客戶端。每個 Elasticsearch 版本會有一個對應的 go-elasticsearch 版本。官方會維護最近的兩個主要版本。

go-elasticsearch 提供了 Low-level 和 Fully-typed 兩套 API。本文以 Fully-typed API 爲例介紹 go-elasticsearch 的常用方法。

關於 Elasticsearch 介紹和如何使用 docker 在本地搭建 Elasticsearch 環境請查看我之前的博客 Elasticsearch 簡明教程。

安裝依賴

執行以下命令安裝 v8 版本的 go 客戶端。

go get github.com/elastic/go-elasticsearch/v8@latest

導入依賴。

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()

連接 ES

指定要連接 ES 的相關配置,並創建客戶端連接。

// 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)
 return
}

操作 ES

本文接下來將以電商平臺 "用戶評價" 數據爲例,演示 Go 語言 Elasticsearch 客戶端的相關操作。

創建 index

創建一個名爲 my-review-1 的 index。

// createIndex 創建索引
func createIndex(client *elasticsearch.TypedClient) {
 resp, err := client.Indices.
  Create("my-review-1").
  Do(context.Background())
 if err != nil {
  fmt.Printf("create index failed, err:%v\n", err)
  return
 }
 fmt.Printf("index:%#v\n", resp.Index)
}

索引 document

定義與 document 數據對應的 ReviewTag 結構體,分別表示 "評價" 和 "評價標籤"。

// 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"`
}

創建一條 document 並添加到 my-review-1 的 index 中。

// indexDocument 索引文檔
func indexDocument(client *elasticsearch.TypedClient) {
 // 定義 document 結構體對象
 d1 := Review{
  ID:      1,
  UserID:  147982601,
  Score:   5,
  Content: "這是一個好評!",
  Tags: []Tag{
   {1000, "好評"},
   {1100, "物超所值"},
   {9000, "有圖"},
  },
  Status:      2,
  PublishTime: time.Now(),
 }

 // 添加文檔
 resp, err := client.Index("my-review-1").
  Id(strconv.FormatInt(d1.ID, 10)).
  Document(d1).
  Do(context.Background())
 if err != nil {
  fmt.Printf("indexing document failed, err:%v\n", err)
  return
 }
 fmt.Printf("result:%#v\n", resp.Result)
}

獲取 document

根據 id 獲取 document。

// getDocument 獲取文檔
func getDocument(client *elasticsearch.TypedClient, id string) {
 resp, err := client.Get("my-review-1", 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_)
}

檢索 document

構建搜索查詢可以使用結構化的查詢條件。

// searchDocument 搜索所有文檔
func searchDocument(client *elasticsearch.TypedClient) {
 // 搜索文檔
 resp, err := client.Search().
  Index("my-review-1").
  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_)
 }
}

下面是在 my-review-1 中搜索 content 包含 "好評" 的文檔。

// searchDocument2 指定條件搜索文檔
func searchDocument2(client *elasticsearch.TypedClient) {
 // 搜索content中包含好評的文檔
 resp, err := client.Search().
  Index("my-review-1").
  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_)
 }
}

聚合

my-review-1 上運行一個平均值聚合,得到所有文檔 score 的平均值。

// aggregationDemo 聚合
func aggregationDemo(client *elasticsearch.TypedClient) {
 avgScoreAgg, err := client.Search().
  Index("my-review-1").
  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"])
}

更新 document

使用新值更新文檔。

// updateDocument 更新文檔
func updateDocument(client *elasticsearch.TypedClient) {
 // 修改後的結構體變量
 d1 := Review{
  ID:      1,
  UserID:  147982601,
  Score:   5,
  Content: "這是一個修改後的好評!", // 有修改
  Tags: []Tag{ // 有修改
   {1000, "好評"},
   {9000, "有圖"},
  },
  Status:      2,
  PublishTime: time.Now(),
 }

 resp, err := client.Update("my-review-1""1").
  Doc(d1). // 使用結構體變量更新
  Do(context.Background())
 if err != nil {
  fmt.Printf("update document failed, err:%v\n", err)
  return
 }
 fmt.Printf("result:%v\n", resp.Result)
}

更新可以使用結構體變量也可以使用原始 JSON 字符串數據。

// updateDocument2 更新文檔
func updateDocument2(client *elasticsearch.TypedClient) {
 // 修改後的JSON字符串
 str := `{
     "id":1,
     "userID":147982601,
     "score":5,
     "content":"這是一個二次修改後的好評!",
     "tags":[
      {
       "code":1000,
       "title":"好評"
      },
      {
       "code":9000,
       "title":"有圖"
      }
     ],
     "status":2,
     "publishDate":"2023-12-10T15:27:18.219385+08:00"
    }`
 // 直接使用JSON字符串更新
 resp, err := client.Update("my-review-1""1").
  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)
}

刪除 document

根據文檔 id 刪除文檔。

// deleteDocument 刪除 document
func deleteDocument(client *elasticsearch.TypedClient) {
 resp, err := client.Delete("my-review-1""1").
  Do(context.Background())
 if err != nil {
  fmt.Printf("delete document failed, err:%v\n", err)
  return
 }
 fmt.Printf("result:%v\n", resp.Result)
}

刪除 index

刪除指定的 index。

// deleteIndex 刪除 index
func deleteIndex(client *elasticsearch.TypedClient) {
 resp, err := client.Indices.
  Delete("my-review-1").
  Do(context.Background())
 if err != nil {
  fmt.Printf("delete document failed, err:%v\n", err)
  return
 }
 fmt.Printf("Acknowledged:%v\n", resp.Acknowledged)
}

參考資料

elasticsearch go-api

elasticsearch go-api examples



本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/Em-xPi2ZqBALiFX9ebb2fw