Skip to content

ElasticSearch

공식 웹사이트: Elasticsearch: 공식 분산 검색 및 분석 엔진 | Elastic

Elasticsearch 는 분산된 RESTful 스타일의 검색 및 데이터 분석 엔진으로, 끊임없이 나타나는 다양한 사용 사례를 해결할 수 있습니다. Elastic Stack 의 핵심으로서 Elasticsearch 는 데이터를 중앙에 저장하여 빠르게 검색하고, 관련성을 미세 조정하며, 강력한 분석을 수행하고, 규모를 쉽게 확장할 수 있도록 합니다. 이 문서에서는 Go 를 사용하여 Elasticsearch 에 대한 기본적인 조작 방법 (예: 추가, 삭제, 수정, 조회 등) 을 설명합니다. Elasticsearch 에 익숙하지 않다면 먼저 직접 학습하시기 바랍니다.

의존성

공식 의존성 라이브러리를 다운로드합니다.

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

ES8 을 사용하는 경우 버전을 변경합니다.

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

TIP

이 문서에서는 ES8 을 사용하여 설명합니다.

연결

elasticsearch.NewClient 함수를 사용하여 새 연결을 설정합니다.

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

ES8+ 는 기본적으로 HTTPS 연결을 사용하며, HTTPS 연결을 설정할 때는 CA 인증서 또는 CA 지문을 사용합니다. 둘 다 Elasticsearch 서버 측에서 생성됩니다. 예제는 다음과 같습니다.

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

elasticsearch에서 제공하는 Go API 는 기본적으로 모두 옵션 스타일 함수입니다. 예를 들어 ping API 를 통해 서비스를 사용할 수 있는지 테스트합니다.

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

출력

[200 OK]

또 다른 예로, Info API 를 통해 서비스 상태를 확인합니다.

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

출력

[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

ES API 와 관련된 문제가 있으면 공식 문서 ES Restful API 를 참조하십시오.

인덱스

Go API 를 통해 인덱스를 조작하며, 인덱스 조작과 관련된 모든 API 는 esapi.Indices 구조체에 위치합니다.

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

생성

인덱스를 생성하며, 다음과 같습니다.

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

실제 조작은 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)
}

출력

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

가져오기

여러 인덱스의 정보를 가져옵니다.

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

출력

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

분석

지정된 인덱스에 대해 텍스트 문자열을 분석하고 결과를 반환합니다. 텍스트는 다음과 같습니다.

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

코드

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

출력

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
    }
  ]
}

삭제

지정된 여러 인덱스를 삭제합니다.

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

출력

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

위의 이러한 API 에서 요청 본문은 수동으로 직렬화해야 하며, 공식에서는 Go 구조체로 매핑되지 않습니다. 응답 본문도 수동으로 처리해야 합니다. 이러한 것은 비교적 흔히 사용되는 API 이며, 다른 것들도 크게 다르지 않습니다.

문서

생성

다음과 같은 문서를 생성합니다.

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

코드

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

출력

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

가져오기

지정된 ID 의 문서를 가져옵니다.

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

출력

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
  }
}

수정

문서 내용을 수정합니다.

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

코드

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

출력

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 는 script 를 지원하여 upsert 등의 작업을 수행할 수 있습니다. 자세한 정보는 Update API 를 참조하십시오.

삭제

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

출력

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

검색

ES API 에서 가장 흔히 사용되는 것은 검색 API 입니다. 아래에서 간단히 사용법을 설명하겠습니다. 먼저 데이터를 준비합니다.

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

검색 API 는 평소 HTTP API 와 완전히 동일하게 사용합니다.

모든 문서 조회

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

특정 필드 매칭

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

요약

기본적인 조작은 대체로 이와 같으며, 사용법은 HTTP API 와 완전히 동일합니다. ES 를 익히면 Go API 조작도 전혀 문제없습니다. cluster, data stream 등 비교적 고급 조작 API 는 직접 탐색해 보시기 바랍니다.

Golang by www.golangdev.cn edit