Skip to content

ElasticSearch

Trang web chính thức: Elasticsearch: Công cụ tìm kiếm và phân tích phân tán chính thức | Elastic

Elasticsearch là một công cụ tìm kiếm và phân tích dữ liệu phân tán theo phong cách RESTful, có khả năng giải quyết nhiều trường hợp sử dụng khác nhau. Là trung tâm của Elastic Stack, Elasticsearch lưu trữ tập trung dữ liệu của bạn, cho phép bạn tìm kiếm nhanh chóng, tinh chỉnh mức độ liên quan, thực hiện phân tích mạnh mẽ và dễ dàng mở rộng quy mô. Bài viết này sẽ giải thích cách thực hiện một số thao tác cơ bản với Elasticsearch bằng Go, chẳng hạn như thêm, xóa, sửa, truy vấn, v.v. Nếu bạn không quen thuộc với Elasticsearch, vui lòng tự học trước.

Phụ thuộc

Tải xuống thư viện chính thức

bash
$ github.com/elastic/go-elasticsearch/v7

Nếu bạn sử dụng ES8, hãy thay đổi phiên bản

bash
$ github.com/elastic/go-elasticsearch/v8

TIP

Bài viết này sử dụng ES8 để minh họa

Kết nối

Sử dụng hàm elasticsearch.NewClient để thiết lập kết nối mới

go
func NewClient(cfg Config) (*Client, error)

ES8+ mặc định sử dụng kết nối HTTPS. Khi thiết lập kết nối HTTPS, bạn có thể sử dụng chứng chỉ CA hoặc dấu vân tay CA, cả hai đều được tạo từ phía máy chủ Elasticsearch. Ví dụ như sau

go
client, err := elasticsearch.NewClient(elasticsearch.Config{
    Addresses:              []string{"https://192.168.153.132:9200"},
    Username:               "elastic",
    Password:               "TETJ8IY+ifbt8SLc+RRQ",
    CertificateFingerprint: "C0E9867C7D446BFF72FE46E7E9FE3455E970A8ADB0D3DF0E1472D55DB2612CD5",
})

Go API do elasticsearch cung cấp về cơ bản đều là các hàm tùy chọn, ví dụ kiểm tra dịch vụ có khả dụng hay không thông qua ping API

go
pingResp, err := client.Ping(client.Ping.WithPretty(), client.Ping.WithHuman())
if err != nil {
    panic(err)
}
fmt.Println(pingResp)

Đầu ra

[200 OK]

Ví dụ khác, kiểm tra trạng thái dịch vụ thông qua Info API

go
infoResp, err := client.Info(client.Info.WithHuman())
if err != nil {
    panic(err)
}
fmt.Println(infoResp)

Đầu ra

[200 OK] {
  "name" : "db-debian12",
  "cluster_name" : "docker-cluster",
  "cluster_uuid" : "OMbDIsNwTFiuyjNF9Xnpbw",
  "version" : {
    "number" : "8.15.0",
    "build_flavor" : "default",
    "build_type" : "docker",
    "build_hash" : "1a77947f34deddb41af25e6f0ddb8e830159c179",
    "build_date" : "2024-08-05T10:05:34.233336849Z",
    "build_snapshot" : false,
    "lucene_version" : "9.11.1",
    "minimum_wire_compatibility_version" : "7.17.0",
    "minimum_index_compatibility_version" : "7.0.0"
  },
  "tagline" : "You Know, for Search"
}

TIP

Về bất kỳ câu hỏi nào liên quan đến ES API, vui lòng tham khảo tài liệu chính thức ES Restful API.

Chỉ mục

Thao tác chỉ mục thông qua go api, tất cả API thao tác chỉ mục đều nằm trong cấu trúc esapi.Indices

go
// Indices contains the Indices APIs
type Indices struct {
    AddBlock              IndicesAddBlock
    Analyze               IndicesAnalyze
    ClearCache            IndicesClearCache
    Clone                 IndicesClone
    Close                 IndicesClose
    ...
    ...
  ValidateQuery         IndicesValidateQuery
}

Tạo

Tạo một chỉ mục như sau

json
{
  "settings": {
    "number_of_shards": 3,
    "number_of_replicas": 2
  },
  "mappings": {
    "properties": {
      "name": {
        "type": "text"
      },
      "age": {
        "type": "long"
      },
      "salary": {
        "type": "double"
      }
    }
  }
}

Thao tác thực tế giống như gửi yêu cầu HTTP

go
func main() {
  client, err := newClient()
  if err != nil {
    panic(err)
  }
  dsl := bytes.NewBufferString(`{
  "settings": {
    "number_of_shards": 3,
    "number_of_replicas": 2
  },
  "mappings": {
    "properties": {
        "name": {
          "type": "text"
        },
        "age": {
          "type": "long"
        },
        "salary": {
          "type": "double"
        }
      }
  }
}`)

  createIndices := client.Indices.Create
  resp, err := createIndices("user", createIndices.WithBody(dsl))
  if err != nil {
    panic(err)
  }
  fmt.Println(resp)
}

Đầu ra

json
[200 OK] {"acknowledged":true,"shards_acknowledged":true,"index":"user"}

Lấy

Lấy thông tin của một số chỉ mục

go
func main() {
  client, err := newClient()
  if err != nil {
    panic(err)
  }

  get := client.Indices.Get
  response, err := get([]string{"user"}, get.WithPretty(), get.WithHuman())
  if err != nil {
    panic(err)
  }
  fmt.Println(response)
}

Đầu ra

json
[200 OK] {
  "user" : {
    "aliases" : { },
    "mappings" : {
      "properties" : {
        "age" : {
          "type" : "long"
        },
        "name" : {
          "type" : "text"
        },
        "salary" : {
          "type" : "double"
        }
      }
    },
    "settings" : {
      "index" : {
        "creation_date_string" : "2024-09-23T04:35:04.528Z",
        "routing" : {
          "allocation" : {
            "include" : {
              "_tier_preference" : "data_content"
            }
          }
        },
        "number_of_shards" : "3",
        "provided_name" : "user",
        "creation_date" : "1727066104528",
        "number_of_replicas" : "2",
        "uuid" : "AvhhuqV2ShGkRP9z7XbdDA",
        "version" : {
          "created_string" : "8.14.4-snapshot[8512000]",
          "created" : "8512000"
        }
      }
    }
  }
}

Phân tích

Phân tích chuỗi văn bản đối với chỉ mục được chỉ định và trả về kết quả, văn bản như sau

json
{
  "analyzer": "standard",
  "text": ["this is a test", "the second text"]
}

Mã nguồn

go
func main() {
  client, err := newClient()
  if err != nil {
    panic(err)
  }

  analyze := client.Indices.Analyze
  dsl := bytes.NewBufferString(`{
  "analyzer" : "standard",
  "text" : ["this is a test", "the second text"]
}`)
  response, err := analyze(analyze.WithIndex("user"), analyze.WithBody(dsl), analyze.WithPretty(), analyze.WithHuman())
  if err != nil {
    panic(err)
  }
  fmt.Println(response)
}

Đầu ra

json
[200 OK] {
  "tokens" : [
    {
      "token" : "this",
      "start_offset" : 0,
      "end_offset" : 4,
      "type" : "<ALPHANUM>",
      "position" : 0
    },
    {
      "token" : "is",
      "start_offset" : 5,
      "end_offset" : 7,
      "type" : "<ALPHANUM>",
      "position" : 1
    },
    {
      "token" : "a",
      "start_offset" : 8,
      "end_offset" : 9,
      "type" : "<ALPHANUM>",
      "position" : 2
    },
    {
      "token" : "test",
      "start_offset" : 10,
      "end_offset" : 14,
      "type" : "<ALPHANUM>",
      "position" : 3
    },
    {
      "token" : "the",
      "start_offset" : 15,
      "end_offset" : 18,
      "type" : "<ALPHANUM>",
      "position" : 104
    },
    {
      "token" : "second",
      "start_offset" : 19,
      "end_offset" : 25,
      "type" : "<ALPHANUM>",
      "position" : 105
    },
    {
      "token" : "text",
      "start_offset" : 26,
      "end_offset" : 30,
      "type" : "<ALPHANUM>",
      "position" : 106
    }
  ]
}

Xóa

Xóa một số chỉ mục được chỉ định

go
func main() {
  client, err := newClient()
  if err != nil {
    panic(err)
  }

  indicesDelete := client.Indices.Delete
  response, err := indicesDelete([]string{"user"})
  if err != nil {
    panic(err)
  }
  fmt.Println(response)
}

Đầu ra

json
[200 OK] {"acknowledged":true}

Trong các API trên, có thể thấy rằng phần thân yêu cầu cần được tuần tự hóa thủ công,官方 không ánh xạ thành cấu trúc Go và phần thân phản hồi cũng cần được xử lý thủ công. Đây là những API thường dùng, các API khác đều tương tự, không có sự khác biệt quá lớn.

Tài liệu

Tạo

Tạo một tài liệu như sau

json
{
  "name": "jack",
  "age": 12,
  "salary": 5701.1
}

Mã nguồn

go
func main() {
  client, err := newClient()
  if err != nil {
    panic(err)
  }

  doc := bytes.NewBufferString(`{
    "name": "jack",
    "age": 12,
    "salary": 5701.1
}`)
  create := client.Create
  response, err := create("user", "1", doc, create.WithPretty())
  if err != nil {
    panic(err)
  }
  fmt.Println(response)
}

Đầu ra

json
[201 Created] {
  "_index" : "user",
  "_id" : "1",
  "_version" : 1,
  "result" : "created",
  "_shards" : {
    "total" : 3,
    "successful" : 1,
    "failed" : 0
  },
  "_seq_no" : 0,
  "_primary_term" : 1
}

Lấy

Lấy một tài liệu với ID được chỉ định

go
func main() {
  client, err := newClient()
  if err != nil {
    panic(err)
  }

  get := client.Get
  response, err := get("user", "1", get.WithPretty())
  if err != nil {
    panic(err)
  }
  fmt.Println(response)
}

Đầu ra

json
[200 OK] {
  "_index" : "user",
  "_id" : "1",
  "_version" : 1,
  "_seq_no" : 0,
  "_primary_term" : 1,
  "found" : true,
  "_source" : {
    "name" : "jack",
    "age" : 12,
    "salary" : 5701.1
  }
}

Cập nhật

Cập nhật nội dung tài liệu

json
{
  "doc": {
    "name": "jack",
    "age": 35,
    "salary": 5701.1
  }
}

Mã nguồn

go
func main() {
  client, err := newClient()
  if err != nil {
    panic(err)
  }

  doc := bytes.NewBufferString(`{
   "doc":  { "name": "jack",
    "age": 35,
    "salary": 5701.1
}}`)
  update := client.Update
  response, err := update("user", "1", doc, update.WithPretty())
  if err != nil {
    panic(err)
  }
  fmt.Println(response)
}

Đầu ra

json
[200 OK] {
  "_index" : "user",
  "_id" : "1",
  "_version" : 2,
  "result" : "updated",
  "_shards" : {
    "total" : 3,
    "successful" : 1,
    "failed" : 0
  },
  "_seq_no" : 1,
  "_primary_term" : 1
}

Update API còn hỗ trợ các thao tác như upsert thông qua script, truy cập Update API để biết thêm thông tin.

Xóa

Xóa một tài liệu được chỉ định theo ID

go
func main() {
    client, err := newClient()
    if err != nil {
       panic(err)
    }

    deleteDoc := client.Delete
    response, err := deleteDoc("user", "1", deleteDoc.WithPretty())
    if err != nil {
       panic(err)
    }
    fmt.Println(response)
}

Đầu ra

json
[200 OK] {
  "_index" : "user",
  "_id" : "1",
  "_version" : 3,
  "result" : "deleted",
  "_shards" : {
    "total" : 3,
    "successful" : 1,
    "failed" : 0
  },
  "_seq_no" : 2,
  "_primary_term" : 1
}

Tìm kiếm

API được sử dụng phổ biến nhất trong ES API là search API. Dưới đây sẽ trình diễn cách sử dụng đơn giản, trước tiên cần chuẩn bị dữ liệu.

go
func main() {
  client, err := newClient()
  if err != nil {
    panic(err)
  }

  for i := range 10 {
    doc := bytes.NewBufferString(fmt.Sprintf(`{
    "name": "%s",
    "age": %d,
    "salary": %f
}`, randomName(), rand.Intn(18)+18, rand.Float64()))
    create := client.Create
    response, err := create("user", string('0'+i), doc, create.WithPretty())
    if err != nil {
      panic(err)
    }
    fmt.Println(response)
  }
}

func randomName() string {
  var b []byte
  for range 10 {
    b = append(b, byte(rand.Intn(26)+'a'))
  }
  return string(b)
}

Search API hoạt động hoàn toàn giống như HTTP API thông thường.

Truy vấn tất cả tài liệu

go
func main() {
  client, err := newClient()
  if err != nil {
    panic(err)
  }
  dsl := bytes.NewBufferString(`{"query": {"match_all":{}}, "size": 1}`)
  search := client.Search
  response, err := search(search.WithBody(dsl), search.WithPretty())
  if err != nil {
    panic(err)
  }
  fmt.Println(response)
}

Khớp một trường

go
func main() {
  client, err := newClient()
  if err != nil {
    panic(err)
  }
  dsl := bytes.NewBufferString(`{"query": {"term":{ "age": 22 } }, "size": 1}`)
  search := client.Search
  response, err := search(search.WithBody(dsl), search.WithPretty())
  if err != nil {
    panic(err)
  }
  fmt.Println(response)
}

Tổng kết

Các thao tác cơ bản gần như là những nội dung trên, cách sử dụng hoàn toàn giống với HTTP API. Khi đã thành thạo ES, việc thao tác với Go API hoàn toàn không có vấn đề gì. Đối với một số thao tác nâng cao như cluster, data stream và các API khác, vui lòng tự khám phá.

Golang by www.golangdev.cn edit