Elasticsearch:运用 Go 语言实现 Elasticsearch 搜索 - 8.x

·  阅读 2042

在我之前的文章 “Elasticsearch:Go 客户端简介 - 8.x”,我对 Elasticsearch golang 客户端做了一个简单的介绍。在今天的这篇文章中,我将详细介绍如何使用这个客户端来一步一步地连接到 Elasticsearch,进而创建索引,搜索等。关于 golang 客户端的使用,完整的文档托管在 GitHubPkgGoDev 上。

在我们的展示中,我们将使用 Elastic Stack 8.5.3 来进行展示。

安装

Elasticsearch 及 Kibana

如果你还没有安装好自己的 Elasticsearch 及 Kibana 的话,那么请参考我之前的文章:

在今天的展示中,我将使用 Elastic Stack 8.x 来进行展示。在安装的时候,请参考相应的 Elastic Stack 8.x 的文章来进行安装。

Golang 安装

要安装客户端的 8.x 版本,请将包添加到你的 go.mod 文件中:

require github.com/elastic/go-elasticsearch/v8 8.5
复制代码

或者,clone 存储库:

git clone --branch 8.5 https://github.com/elastic/go-elasticsearch.git $GOPATH/src/github
复制代码

要安装另一个版本,请相应地修改路径或分支名称。 客户端主要版本对应于 Elasticsearch 主要版本。

你可以在下面找到完整的安装示例:

`

1.  mkdir my-elasticsearch-app8 && cd my-elasticsearch-app8

3.  cat > go.mod <<-END
4.    module my-elasticsearch-app8

6.    require github.com/elastic/go-elasticsearch/v8 main
7.  END

9.  cat > main.go <<-END
10.    package main

12.    import (
13.      "log"

15.      "github.com/elastic/go-elasticsearch/v8"
16.    )

18.    func main() {
19.      es, _ := elasticsearch.NewDefaultClient()
20.      log.Println(elasticsearch.Version)
21.      log.Println(es.Info())
22.    }
23.  END

`![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
复制代码

在我的电脑上面,我运行如上的命令:

`

1.  $ pwd
2.  /Users/liuxg/go
3.  $ mkdir my-elasticsearch-app8 && cd my-elasticsearch-app8
4.  $ 
5.  $ cat > go.mod <<-END
6.  >   module my-elasticsearch-app8
7.  > 
8.  >   require github.com/elastic/go-elasticsearch/v8 main
9.  > END
10.  $ 
11.  $ cat > main.go <<-END
12.  >   package main
13.  > 
14.  >   import (
15.  >     "log"
16.  > 
17.  >     "github.com/elastic/go-elasticsearch/v8"
18.  >   )
19.  > 
20.  >   func main() {
21.  >     es, _ := elasticsearch.NewDefaultClient()
22.  >     log.Println(elasticsearch.Version)
23.  >     log.Println(es.Info())
24.  >   }
25.  > END
26.  $ ls
27.  go.mod  main.go
28.  $ pwd
29.  /Users/liuxg/go/my-elasticsearch-app8

`![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
复制代码

很显然,它生成了两个文件:go.mod 及 main.go。我们还不能直接运行上面的命令,除非我们按照我之前的文章 “Elastic Stack 8.0 安装 - 保护你的 Elastic Stack 现在比以往任何时候都简单” 进行安装。请参考其中的 “如何配置 Elasticsearch 不带安全性” 章节。这样的配置不需要安全性,索引在连接的时候,我们也不需要任何的验证。一旦我们按照完毕后,我们在 terminal 中打入如下的命令:

`

1.  $ pwd
2.  /Users/liuxg/go/my-elasticsearch-app8
3.  $ go run main.go
4.  go: updates to go.mod needed; to update it:
5.  	go mod tidy
6.  $ go mod tidy
7.  go: downloading github.com/elastic/go-elasticsearch/v8 v8.4.0-alpha.1.0.20221227164349-c40d762a40ad
8.  go: downloading github.com/elastic/elastic-transport-go/v8 v8.0.0-20211216131617-bbee439d559c
9.  $ go run main.go
10.  2023/01/10 17:27:35 8.7.0-SNAPSHOT
11.  2023/01/10 17:27:35 [200 OK] {
12.    "name" : "liuxgm.local",
13.    "cluster_name" : "elasticsearch",
14.    "cluster_uuid" : "c7GQIJYaQ-yeesPYys24fw",
15.    "version" : {
16.      "number" : "8.5.3",
17.      "build_flavor" : "default",
18.      "build_type" : "tar",
19.      "build_hash" : "4ed5ee9afac63de92ec98f404ccbed7d3ba9584e",
20.      "build_date" : "2022-12-05T18:22:22.226119656Z",
21.      "build_snapshot" : false,
22.      "lucene_version" : "9.4.2",
23.      "minimum_wire_compatibility_version" : "7.17.0",
24.      "minimum_index_compatibility_version" : "7.0.0"
25.    },
26.    "tagline" : "You Know, for Search"
27.  }
28.   <nil>

`![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
复制代码

上面显示我们已经成功地连接到 Elasticsearch 了。Hooray! 小试牛刀,我们对如何连接到 Elasticsearch 有一个基本的印象。

在默认的情况下,我们可以通过设置环境变量 ELASTICSEARCH_URL 来配置 Elasticsearch 的端点地址:

 export ELASTICSEARCH_URL="https://localhost:9200"
复制代码

如果你有多个 Elasticsearch 端点地址,请用逗号分隔它们。

警告:不建议在未启用安全性的情况下运行 Elasticsearch。

Elasticsearch 版本兼容性

语言客户端向前兼容; 这意味着客户端支持与更大或相等的次要版本的 Elasticsearch 进行通信。 Elasticsearch 语言客户端仅向后兼容默认发行版,并且不作任何保证。

Elasticsearch server 8.0 版本引入了新的兼容模式,让你从 7.x 到 8x 的升级体验更流畅。简而言之,你可以将最新的 7.x go-elasticsearch Elasticsearch 客户端与 8.x Elasticsearch 服务器一起使用,提供更多协调将代码库升级到下一个主要版本的空间。

如果你想利用此功能,请确保你使用的是最新的 7.x go-elasticsearch 客户端,并将环境变量 ELASTIC_CLIENT_APIVERSIONING 设置为 true 或在客户端配置中设置配置选项 config.EnableCompatibilityMode。 客户端在内部处理其余部分。 对于每个 8.0 及更高版本的 go-elasticsearch 客户端,你都准备好了! 默认情况下启用兼容模式。

使用 Go 模块时,在导入路径中包含版本,并指定显式版本或分支:



1.  require github.com/elastic/go-elasticsearch/v8 v8.0.0
2.  require github.com/elastic/go-elasticsearch/v7 7.17


复制代码

可以在一个项目中使用多个版本的客户端:



1.  // go.mod
2.  github.com/elastic/go-elasticsearch/v7 v7.17.0
3.  github.com/elastic/go-elasticsearch/v8 v8.0.0

5.  // main.go
6.  import (
7.    elasticsearch7 "github.com/elastic/go-elasticsearch/v7"
8.    elasticsearch8 "github.com/elastic/go-elasticsearch/v8"
9.  )
10.  // ...
11.  es7, _ := elasticsearch7.NewDefaultClient()
12.  es8, _ := elasticsearch8.NewDefaultClient()


复制代码

客户端的 main 分支兼容当前 Elasticsearch 的 master 分支。

连接到 Elasticsearch

在今天的文章中,我主要来讲述如何连接到自托管的 Elasticsearch 集群。

连接到没有设置安全的集群

在上面,我们已经显示了如何连接到没有设置安全的集群。在上面,它默认连接的是 http://localhost:9200。在实际的部署中,你可能并不是使用的默认的地址。这个时候我们需要对连接进行配置:

main.go

`

1.  package main

3.  import (
4.  	"log"

6.  	"github.com/elastic/go-elasticsearch/v8"
7.  )

9.  func main() {
10.  	cfg := elasticsearch.Config{
11.  		Addresses: []string{
12.  			"http://localhost:9200",
13.  		},
14.  	}
15.  	es, err := elasticsearch.NewClient(cfg)
16.  	log.Println(err)
17.  	if err == nil {
18.  		log.Println(elasticsearch.Version)
19.  		log.Println(es.Info())
20.  	} else {
21.  		log.Println("Something wrong with connection to Elasticsearch")
22.  	}
23.  }

`![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
复制代码
`

1.  $ pwd
2.  /Users/liuxg/go/my-elasticsearch-app8
3.  $ ls
4.  go.mod  go.sum  main.go
5.  $ go run main.go 
6.  2023/01/10 19:21:49 <nil>
7.  2023/01/10 19:21:49 8.7.0-SNAPSHOT
8.  2023/01/10 19:21:49 [200 OK] {
9.    "name" : "liuxgm.local",
10.    "cluster_name" : "elasticsearch",
11.    "cluster_uuid" : "c7GQIJYaQ-yeesPYys24fw",
12.    "version" : {
13.      "number" : "8.5.3",
14.      "build_flavor" : "default",
15.      "build_type" : "tar",
16.      "build_hash" : "4ed5ee9afac63de92ec98f404ccbed7d3ba9584e",
17.      "build_date" : "2022-12-05T18:22:22.226119656Z",
18.      "build_snapshot" : false,
19.      "lucene_version" : "9.4.2",
20.      "minimum_wire_compatibility_version" : "7.17.0",
21.      "minimum_index_compatibility_version" : "7.0.0"
22.    },
23.    "tagline" : "You Know, for Search"
24.  }
25.   <nil>

`![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
复制代码

从上面,我们可以看出来,我们成功地连接到 Elasticsearch。此外,由于 Addresses 是一个 slice,它可以由多个 Elasticsearch 的端点组成。比如,我们可以有一下的格式:



1.  cfg := elasticsearch.Config{
2.          Addresses: []string{
3.              "http://localhost:9200",
4.              "http://localhost:9201",
5.          },
6.  }
7.  es, err := elasticsearch.NewClient(cfg)


复制代码

注意:如果你的 Elasticsearch 集群位于负载均衡器后面,就像在使用 Elastic Cloud 时一样,你将不需要配置多个节点。 而是使用负载平衡器主机和端口。

连接到带有基本安全的集群

我们可以连接到带有基本安全的集群。针对 Elastic Stack 8.x,在默认的安装下,集群是带有 HTTPS 的访问。我们可以通过参考文章  “Elastic Stack 8.0 安装 - 保护你的 Elastic Stack 现在比以往任何时候都简单” 中的 “如何配置 Elasticsearch 只带有基本安全” 章节来进行安装。

基本认证

要以编程方式设置集群端点、用户名和密码,请将配置对象传递给 elasticsearch.NewClient() 函数。

main.go

`

1.  package main

3.  import (
4.  	"log"

6.  	"github.com/elastic/go-elasticsearch/v8"
7.  )

9.  func main() {
10.  	cfg := elasticsearch.Config{
11.  		Addresses: []string{
12.  			"https://localhost:9200",
13.  		},
14.  		Username: "elastic",
15.  		Password: "password",
16.  	}

18.  	es, err := elasticsearch.NewClient(cfg)
19.  	log.Println(err)
20.  	if err == nil {
21.  		log.Println(elasticsearch.Version)
22.  		log.Println(es.Info())
23.  	} else {
24.  		log.Println("Something wrong with connection to Elasticsearch")
25.  	}
26.  }

`![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
复制代码

在上面,我使用了超级用户 elastic 来进行验证,尽管在实际的使用中,我们并不建议这么做。你可以使用一个带有一定权限的用户来进行连接。运行上面的代码:

go run main.go 
复制代码
`

1.  $ go run main.go 
2.  2023/01/10 19:44:29 <nil>
3.  2023/01/10 19:44:29 8.7.0-SNAPSHOT
4.  2023/01/10 19:44:29 [200 OK] {
5.    "name" : "liuxgm.local",
6.    "cluster_name" : "elasticsearch",
7.    "cluster_uuid" : "jBt9oXsxT4y_2YOWOw8QRQ",
8.    "version" : {
9.      "number" : "8.5.3",
10.      "build_flavor" : "default",
11.      "build_type" : "tar",
12.      "build_hash" : "4ed5ee9afac63de92ec98f404ccbed7d3ba9584e",
13.      "build_date" : "2022-12-05T18:22:22.226119656Z",
14.      "build_snapshot" : false,
15.      "lucene_version" : "9.4.2",
16.      "minimum_wire_compatibility_version" : "7.17.0",
17.      "minimum_index_compatibility_version" : "7.0.0"
18.    },
19.    "tagline" : "You Know, for Search"
20.  }
21.   <nil>

`![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
复制代码

很显然,我们的连接是成功的。

你还可以在端点 URL 中包含用户名和密码:

main.go

`

1.  package main

3.  import (
4.  	"log"

6.  	"github.com/elastic/go-elasticsearch/v8"
7.  )

9.  func main() {
10.  	cfg := elasticsearch.Config{
11.  		Addresses: []string{
12.  			"http://elastic:password@localhost:9200",
13.  		},
14.  	}

16.  	es, err := elasticsearch.NewClient(cfg)
17.  	log.Println(err)
18.  	if err == nil {
19.  		log.Println(elasticsearch.Version)
20.  		log.Println(es.Info())
21.  	} else {
22.  		log.Println("Something wrong with connection to Elasticsearch")
23.  	}
24.  }

`![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
复制代码

连接到带有 HTTPS 的集群

在 Elastic Stack 8.x 的默认安装中,Elasticsearch 是带有 HTTPS 的访问权限的。特别是针对自签名证书的安装,我们需要使用证书来进行连接。请按照如下的文档进行安装:

在 Elasticsearch 第一次启动的时候:

我们从上面可以看到超级用户 elastic 的信息。记下 elastic 用户密码和 HTTP CA 指纹。我们在下面的示例中将使用到。

根据具体情况,有两种验证 HTTPS 连接的选项,要么使用 CA 证书本身进行验证,要么通过 HTTP CA 证书指纹进行验证。

使用 CA 证书来验证 HTTPS

生成的根 CA 证书可以在 Elasticsearch 配置位置 ($ES_CONF_PATH/certs/http_ca.crt) 的 certs 目录中找到。 如果你在 Docker 中运行 Elasticsearch,则还有用于检索 CA 证书的其他文档。一旦你在某个地方获得了 http_ca.crt 文件,就可以通过 CACert 将文件的内容传递给客户端:

我们可以在 Elasticsearch 的安装目录中查看到证书的信息:



1.  $ pwd
2.  /Users/liuxg/elastic/elasticsearch-8.5.3/config/certs
3.  $ ls
4.  http.p12      http_ca.crt   transport.p12


复制代码

我们可以通过如下的方式来连接到 Elasticsearch:

main.go

`

1.  package main

3.  import (
4.  	"io/ioutil"
5.  	"log"

7.  	"github.com/elastic/go-elasticsearch/v8"
8.  )

10.  func main() {
11.  	cert, _ := ioutil.ReadFile("/Users/liuxg/elastic/elasticsearch-8.5.3/config/certs/http_ca.crt")

13.  	cfg := elasticsearch.Config{
14.  		Addresses: []string{
15.  			"https://localhost:9200",
16.  		},
17.  		Username: "elastic",
18.  		Password: "YQ7kq-gh3K4xi9l4akd-",
19.  		CACert:   cert,
20.  	}

22.  	es, err := elasticsearch.NewClient(cfg)
23.  	log.Println(err)

25.  	if err == nil {
26.  		log.Println(elasticsearch.Version)
27.  		log.Println(es.Info())
28.  	} else {
29.  		log.Println("Something wrong with connection to Elasticsearch")
30.  	}
31.  }

`![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
复制代码

运行上面的代码,我们可以看到和上面一样的运行结果。

使用证书 fingerprint 来验证 HTTPS

这种验证 HTTPS 连接的方法利用了前面记下的证书指纹值。 获取此 SHA256 指纹值并通过 ca_fingerprint 将其传递给 Go Elasticsearch 客户端:

main.go

`

1.  package main

3.  import (
4.  	"log"

6.  	"github.com/elastic/go-elasticsearch/v8"
7.  )

9.  func main() {
10.  	cfg := elasticsearch.Config{
11.  		Addresses: []string{
12.  			"https://localhost:9200",
13.  		},
14.  		Username:               "elastic",
15.  		Password:               "YQ7kq-gh3K4xi9l4akd-",
16.  		CertificateFingerprint: "2d1bcafa3cb22f6a0c4b2c087409c6b0b59017d444c49456fe9e87c0c6a2db60",
17.  	}

19.  	es, err := elasticsearch.NewClient(cfg)
20.  	log.Println(err)

22.  	if err == nil {
23.  		log.Println(elasticsearch.Version)
24.  		log.Println(es.Info())
25.  	} else {
26.  		log.Println("Something wrong with connection to Elasticsearch")
27.  	}
28.  }

`![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
复制代码

我们运行上面的代码,我们可以看到和之前输出一样的结果。

可以使用带有证书文件的 openssl x509 计算证书指纹:

openssl x509 -fingerprint -sha256 -noout -in /path/to/http_ca.crt
复制代码


1.  $ pwd
2.  /Users/liuxg/elastic/elasticsearch-8.5.3/config/certs
3.  $ ls
4.  http.p12      http_ca.crt   transport.p12
5.  $ openssl x509 -fingerprint -sha256 -noout -in http_ca.crt
6.  sha256 Fingerprint=2D:1B:CA:FA:3C:B2:2F:6A:0C:4B:2C:08:74:09:C6:B0:B5:90:17:D4:44:C4:94:56:FE:9E:87:C0:C6:A2:DB:60


复制代码

如果你无权访问 Elasticsearch 生成的 CA 文件,你可以使用以下脚本通过 openssl s_client 输出 Elasticsearch 实例的根 CA 指纹:



1.  # Replace the values of 'localhost' and '9200' to the
2.  # corresponding host and port values for the cluster.
3.  openssl s_client -connect localhost:9200 -servername localhost -showcerts </dev/null 2>/dev/null \
4.    | openssl x509 -fingerprint -sha256 -noout -in /dev/stdin


复制代码


1.  $ openssl s_client -connect localhost:9200 -servername localhost -showcerts </dev/null 2>/dev/null \
2.  >   | openssl x509 -fingerprint -sha256 -noout -in /dev/stdin
3.  sha256 Fingerprint=92:54:07:A7:BF:FE:AA:6C:A9:4C:17:7E:A8:E7:7D:F9:B7:27:2E:99:BF:DC:9C:D0:51:D1:9F:F2:2E:D7:9A:4A


复制代码

在上面的代码中,千万要注意的是我们代码中的 fingerprint 是没有冒号的。我们可以使用如下的命令来直接进行获得:



1.  $ pwd
2.  /Users/liuxg/elastic/elasticsearch-8.5.3/config/certs
3.  $ ls
4.  http.p12      http_ca.crt   transport.p12
5.  $ openssl x509 -in http_ca.crt -sha256 -fingerprint | grep sha256 | sed 's/://g'
6.  sha256 Fingerprint=2D1BCAFA3CB22F6A0C4B2C087409C6B0B59017D444C49456FE9E87C0C6A2DB60


复制代码

HTTP Bearer 认证

HTTP Bearer 身份验证通过将令牌作为字符串传递来使用 ServiceToken 参数。 此身份验证方法由服务帐户令牌和不记名令牌使用。关于如何生成 service token,请参考我之前的文章 “Elasticsearch:无需基本身份验证即可创建用于访问的不记名令牌”。



1.  POST /_security/oauth2/token
2.  {
3.    "grant_type": "client_credentials"
4.  }


复制代码

 main.go

`

1.  package main

3.  import (
4.  	"log"

6.  	"github.com/elastic/go-elasticsearch/v8"
7.  )

9.  func main() {
10.  	cfg := elasticsearch.Config{
11.  		Addresses: []string{
12.  			"https://localhost:9200",
13.  		},
14.  		CertificateFingerprint: "d293735f339412c738c5a258fe950fb2fcdaa33f772365a948abbb332a7c58b4",
15.  		ServiceToken:           "363rAxZZbmFaTnROSVRjT1Q0ZEVmQmszelhRAAAAAAAAAAAA",
16.  	}

18.  	es, err := elasticsearch.NewClient(cfg)
19.  	log.Println(err)

21.  	if err == nil {
22.  		log.Println(elasticsearch.Version)
23.  		log.Println(es.Info())
24.  	} else {
25.  		log.Println("Something wrong with connection to Elasticsearch")
26.  	}
27.  }

`![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
复制代码

我们运行上面的代码,它会输出和上面一样的结果。我们或者使用如下的格式:

`

1.  package main

3.  import (
4.  	"log"
5.  	"net/http"

7.  	"github.com/elastic/go-elasticsearch/v8"
8.  )

10.  func main() {
11.  	cfg := elasticsearch.Config{
12.  		Addresses: []string{
13.  			"https://localhost:9200",
14.  		},
15.  		CertificateFingerprint: "d293735f339412c738c5a258fe950fb2fcdaa33f772365a948abbb332a7c58b4",
16.  		Header: http.Header(map[string][]string{
17.  			"Authorization": {"Bearer 363rAxZsR0Q0RDMzb1MtaXU1alJPMnFHMjZ3AAAAAAAAAAAA"},
18.  		}),
19.  	}

21.  	es, err := elasticsearch.NewClient(cfg)
22.  	log.Println(err)

24.  	if err == nil {
25.  		log.Println(elasticsearch.Version)
26.  		log.Println(es.Info())
27.  	} else {
28.  		log.Println("Something wrong with connection to Elasticsearch")
29.  	}
30.  }

`![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
复制代码

在上面,我们使用 Bearer 在 header 中的定义来实现请求。

API key 验证

我们也可以使用 API key 的方法来进行验证。我们可以参考文章 “Elasticsearch:创建 API key 接口访问 Elasticsearch” 来获取  API key。我们也可以使用如下的方法来获取 API key:

 

 

 

点击上面的 copy 按钮。拷贝生成的 API key。我们把这个 API key 应用到如下的代码中:

main.go

`

1.  package main

3.  import (
4.  	"log"

6.  	"github.com/elastic/go-elasticsearch/v8"
7.  )

9.  func main() {
10.  	cfg := elasticsearch.Config{
11.  		Addresses: []string{
12.  			"https://localhost:9200",
13.  		},
14.  		CertificateFingerprint: "d293735f339412c738c5a258fe950fb2fcdaa33f772365a948abbb332a7c58b4",
15.  		APIKey:                 "dTM5Tm40VUJ2OUFNdUFLUDNnRUU6NWQ2QzhTZmhURk82MUFsUFE0a2ltUQ==",
16.  	}

18.  	es, err := elasticsearch.NewClient(cfg)
19.  	log.Println(err)

21.  	if err == nil {
22.  		log.Println(elasticsearch.Version)
23.  		log.Println(es.Info())
24.  	} else {
25.  		log.Println("Something wrong with connection to Elasticsearch")
26.  	}
27.  }

`![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
复制代码

 运行上面的代码。它将成功地连接到 Elasticsearch 集群。

Retries

我们已经了解了客户端如何管理连接并针对特定条件重试请求。 现在让我们看看相关的配置选项。

默认情况下,客户端最多重试请求 3 次; 要设置不同的限制,请使用 MaxRetries 字段。 要更改应重试的响应状态代码列表,请使用 RetryOnStatus 字段。 与 RetryBackoff 选项一起,您可以使用它在服务器发送 429 Too Many Requests 响应时重试请求:

main.go

`

1.  package main

3.  import (
4.  	"log"
5.  	"math"
6.  	"time"

8.  	"github.com/elastic/go-elasticsearch/v8"
9.  )

11.  func main() {
12.  	cfg := elasticsearch.Config{
13.  		Addresses: []string{
14.  			"https://localhost:9200",
15.  		},
16.  		CertificateFingerprint: "d293735f339412c738c5a258fe950fb2fcdaa33f772365a948abbb332a7c58b4",
17.  		APIKey:                 "dTM5Tm40VUJ2OUFNdUFLUDNnRUU6NWQ2QzhTZmhURk82MUFsUFE0a2ltUQ==",
18.  		RetryOnStatus:          []int{429, 502, 503, 504},
19.  		RetryBackoff: func(i int) time.Duration {
20.  			// A simple exponential delay
21.  			d := time.Duration(math.Exp2(float64(i))) * time.Second
22.  			log.Printf("Attempt: %d | Sleeping for %s...\n", i, d)
23.  			return d
24.  		},
25.  	}

27.  	es, err := elasticsearch.NewClient(cfg)
28.  	log.Println(err)

30.  	if err == nil {
31.  		log.Println(elasticsearch.Version)
32.  		log.Println(es.Info())
33.  	} else {
34.  		log.Println("Something wrong with connection to Elasticsearch")
35.  	}
36.  }

`![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
复制代码

配置其它 HTTP 设置

要配置其他 HTTP 设置,请在配置对象中传递一个 http.Transport 对象。



1.  cfg := elasticsearch.Config{
2.    Transport: &http.Transport{
3.      MaxIdleConnsPerHost:   10,
4.      ResponseHeaderTimeout: time.Second,
5.      TLSClientConfig: &tls.Config{
6.        MinVersion: tls.VersionTLS12,
7.        // ...
8.      },
9.      // ...
10.    },
11.  }


复制代码

有关客户端配置和自定义的更多示例,请参阅 _examples/configuration.go_examples/customization.go 文件。 有关安全配置的示例,请参阅 _examples/security

完整例子

以下示例演示了更复杂的用法。 它从集群中获取 Elasticsearch 版本,同时索引几个文档,并使用响应主体周围的轻量级包装器打印搜索结果。我们从上面的代码作为基础进行编码:

main.go

`

1.  package main

3.  import (
4.  	"encoding/json"
5.  	"log"
6.  	"math"
7.  	"time"

9.  	"github.com/elastic/go-elasticsearch/v8"
10.  )

12.  func main() {
13.  	cfg := elasticsearch.Config{
14.  		Addresses: []string{
15.  			"https://localhost:9200",
16.  		},
17.  		CertificateFingerprint: "d293735f339412c738c5a258fe950fb2fcdaa33f772365a948abbb332a7c58b4",
18.  		APIKey:                 "dTM5Tm40VUJ2OUFNdUFLUDNnRUU6NWQ2QzhTZmhURk82MUFsUFE0a2ltUQ==",
19.  		RetryOnStatus:          []int{429, 502, 503, 504},
20.  		RetryBackoff: func(i int) time.Duration {
21.  			// A simple exponential delay
22.  			d := time.Duration(math.Exp2(float64(i))) * time.Second
23.  			log.Printf("Attempt: %d | Sleeping for %s...\n", i, d)
24.  			return d
25.  		},
26.  	}

28.  	log.SetFlags(0)

30.  	var (
31.  		r map[string]interface{}
32.  		// wg sync.WaitGroup
33.  	)

35.  	es, err := elasticsearch.NewClient(cfg)
36.  	log.Println(err)
37.  	if err == nil {
38.  		log.Println("Successfully connected to Elasticsearch!")
39.  	}
40.  	// 1. Get cluster info
41.  	//
42.  	res, err := es.Info()
43.  	if err != nil {
44.  		log.Fatalf("Error getting response: %s", err)
45.  	}
46.  	defer res.Body.Close()

48.  	// Check response status
49.  	if res.IsError() {
50.  		log.Fatalf("Error: %s", res.String())
51.  	}

53.  	// Deserialize the response into a map.
54.  	if err := json.NewDecoder(res.Body).Decode(&r); err != nil {
55.  		log.Fatalf("Error parsing the response body: %s", err)
56.  	}
57.  }

`![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
复制代码

运行上面的代码:

go run main.go
复制代码

上面的代码运行的结果为:



1.  $ go run main.go
2.  <nil>
3.  Successfully connected to Elasticsearch!


复制代码

它显示我们的连接到 Elasticsearch 是成功的。

我们接下来打印运行的结果:

 1.    // Print client and server version numbers.
2.    log.Printf("Client: %s", elasticsearch.Version)
3.    log.Printf("Server: %s", r["version"].(map[string]interface{})["number"])
4.    log.Println(strings.Repeat("~", 37))
复制代码


1.  $ go run main.go
2.  <nil>
3.  Successfully connected to Elasticsearch!
4.  Client: 8.7.0-SNAPSHOT
5.  Server: 8.5.3
6.  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~


复制代码

我们接下来运行如下的命令来下载 esapi 包:

go get github.com/elastic/go-elasticsearch/v8/esapi
复制代码

main.go

`

1.  package main

3.  import (
4.  	"bytes"
5.  	"context"
6.  	"encoding/json"
7.  	"log"
8.  	"math"
9.  	"strconv"
10.  	"strings"
11.  	"sync"
12.  	"time"

14.  	"github.com/elastic/go-elasticsearch/v8"
15.  	"github.com/elastic/go-elasticsearch/v8/esapi"
16.  )

18.  func main() {
19.  	cfg := elasticsearch.Config{
20.  		Addresses: []string{
21.  			"https://localhost:9200",
22.  		},
23.  		CertificateFingerprint: "d293735f339412c738c5a258fe950fb2fcdaa33f772365a948abbb332a7c58b4",
24.  		APIKey:                 "dTM5Tm40VUJ2OUFNdUFLUDNnRUU6NWQ2QzhTZmhURk82MUFsUFE0a2ltUQ==",
25.  		RetryOnStatus:          []int{429, 502, 503, 504},
26.  		RetryBackoff: func(i int) time.Duration {
27.  			// A simple exponential delay
28.  			d := time.Duration(math.Exp2(float64(i))) * time.Second
29.  			log.Printf("Attempt: %d | Sleeping for %s...\n", i, d)
30.  			return d
31.  		},
32.  	}

34.  	log.SetFlags(0)

36.  	var (
37.  		r map[string]interface{}
38.  		// wg sync.WaitGroup
39.  	)

41.  	es, err := elasticsearch.NewClient(cfg)
42.  	log.Println(err)
43.  	if err == nil {
44.  		log.Println("Successfully connected to Elasticsearch!")
45.  	}
46.  	// 1. Get cluster info
47.  	//
48.  	res, err := es.Info()
49.  	if err != nil {
50.  		log.Fatalf("Error getting response: %s", err)
51.  	}
52.  	defer res.Body.Close()

54.  	// Check response status
55.  	if res.IsError() {
56.  		log.Fatalf("Error: %s", res.String())
57.  	}

59.  	// Deserialize the response into a map.
60.  	if err := json.NewDecoder(res.Body).Decode(&r); err != nil {
61.  		log.Fatalf("Error parsing the response body: %s", err)
62.  	}

64.  	// Print client and server version numbers.
65.  	log.Printf("Client: %s", elasticsearch.Version)
66.  	log.Printf("Server: %s", r["version"].(map[string]interface{})["number"])
67.  	log.Println(strings.Repeat("~", 37))

69.  	var wg sync.WaitGroup
70.  	for i, title := range []string{"Test One", "Test Two"} {
71.  		wg.Add(1)

73.  		go func(i int, title string) {
74.  			defer wg.Done()

76.  			// Build the request body.
77.  			data, err := json.Marshal(struct{ Title string }{Title: title})
78.  			if err != nil {
79.  				log.Fatalf("Error marshaling document: %s", err)
80.  			}

82.  			// Set up the request object.
83.  			req := esapi.IndexRequest{
84.  				Index:      "test",
85.  				DocumentID: strconv.Itoa(i + 1),
86.  				Body:       bytes.NewReader(data),
87.  				Refresh:    "true",
88.  			}

90.  			// Perform the request with the client.
91.  			res, err := req.Do(context.Background(), es)
92.  			if err != nil {
93.  				log.Fatalf("Error getting response: %s", err)
94.  			}
95.  			defer res.Body.Close()

97.  			if res.IsError() {
98.  				log.Printf("[%s] Error indexing document ID=%d", res.Status(), i+1)
99.  			} else {
100.  				// Deserialize the response into a map.
101.  				var r map[string]interface{}
102.  				if err := json.NewDecoder(res.Body).Decode(&r); err != nil {
103.  					log.Printf("Error parsing the response body: %s", err)
104.  				} else {
105.  					// Print the response status and indexed document version.
106.  					log.Printf("[%s] %s; version=%d", res.Status(), r["result"], int(r["_version"].(float64)))
107.  				}
108.  			}
109.  		}(i, title)
110.  	}
111.  	wg.Wait()
112.  	log.Println(strings.Repeat("-", 37))

114.  }

`![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
复制代码

运行上面的代码:



1.  <nil>
2.  Successfully connected to Elasticsearch!
3.  Client: 8.7.0-SNAPSHOT
4.  Server: 8.5.3
5.  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
6.  [201 Created] created; version=1
7.  [201 Created] created; version=1
8.  -------------------------------------


复制代码

上面的代码在 Elasticsearch 中创建一个叫做 test 的索引,并向里面写入两个文档。我们可以在 Kibana 中进行查看:

GET test/_search?filter_path=**.hits
复制代码

 接下来,我们添加如下的代码来进行搜索:

 `1.    // 3. Search for the indexed documents
2.    //
3.    // Build the request body.
4.    var buf bytes.Buffer
5.    query := map[string]interface{}{
6.      "query": map[string]interface{}{
7.        "match": map[string]interface{}{
8.          "Title": "test",
9.        },
10.      },
11.    }
12.    if err := json.NewEncoder(&buf).Encode(query); err != nil {
13.      log.Fatalf("Error encoding query: %s", err)
14.    }

16.    // Perform the search request.
17.    res, err = es.Search(
18.      es.Search.WithContext(context.Background()),
19.      es.Search.WithIndex("test"),
20.      es.Search.WithBody(&buf),
21.      es.Search.WithTrackTotalHits(true),
22.      es.Search.WithPretty(),
23.    )
24.    if err != nil {
25.      log.Fatalf("Error getting response: %s", err)
26.    }
27.    defer res.Body.Close()

29.    if res.IsError() {
30.      var e map[string]interface{}
31.      if err := json.NewDecoder(res.Body).Decode(&e); err != nil {
32.        log.Fatalf("Error parsing the response body: %s", err)
33.      } else {
34.        // Print the response status and error information.
35.        log.Fatalf("[%s] %s: %s",
36.          res.Status(),
37.          e["error"].(map[string]interface{})["type"],
38.          e["error"].(map[string]interface{})["reason"],
39.        )
40.      }
41.    }

43.    if err := json.NewDecoder(res.Body).Decode(&r); err != nil {
44.      log.Fatalf("Error parsing the response body: %s", err)
45.    }
46.    // Print the response status, number of results, and request duration.
47.    log.Printf(
48.      "[%s] %d hits; took: %dms",
49.      res.Status(),
50.      int(r["hits"].(map[string]interface{})["total"].(map[string]interface{})["value"].(float64)),
51.      int(r["took"].(float64)),
52.    )
53.    // Print the ID and document source for each hit.
54.    for _, hit := range r["hits"].(map[string]interface{})["hits"].([]interface{}) {
55.      log.Printf(" * ID=%s, %s", hit.(map[string]interface{})["_id"], hit.(map[string]interface{})["_source"])
56.    }

58.    log.Println(strings.Repeat("=", 37))`![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
复制代码

运行上面的结果:



1.  $ go run main.go
2.  <nil>
3.  Successfully connected to Elasticsearch!
4.  Client: 8.7.0-SNAPSHOT
5.  Server: 8.5.3
6.  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
7.  [200 OK] updated; version=4
8.  [200 OK] updated; version=4
9.  -------------------------------------
10.  [200 OK] 2 hits; took: 0ms
11.   * ID=1, map[Title:Test One]
12.   * ID=2, map[Title:Test Two]
13.  =====================================


复制代码

上面的搜索相当于如下的搜索:



1.  GET test/_search
2.  {
3.    "query": {
4.      "match": {
5.        "Title": "test"
6.      }
7.    }
8.  }


复制代码

它显示的搜索结果为:

`

1.  {
2.    "took": 0,
3.    "timed_out": false,
4.    "_shards": {
5.      "total": 1,
6.      "successful": 1,
7.      "skipped": 0,
8.      "failed": 0
9.    },
10.    "hits": {
11.      "total": {
12.        "value": 2,
13.        "relation": "eq"
14.      },
15.      "max_score": 0.074107975,
16.      "hits": [
17.        {
18.          "_index": "test",
19.          "_id": "1",
20.          "_score": 0.074107975,
21.          "_source": {
22.            "Title": "Test One"
23.          }
24.        },
25.        {
26.          "_index": "test",
27.          "_id": "2",
28.          "_score": 0.074107975,
29.          "_source": {
30.            "Title": "Test Two"
31.          }
32.        }
33.      ]
34.    }
35.  }

`![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
复制代码

最终的代码为:

main.go

`

1.  package main

3.  import (
4.  	"bytes"
5.  	"context"
6.  	"encoding/json"
7.  	"log"
8.  	"math"
9.  	"strconv"
10.  	"strings"
11.  	"sync"
12.  	"time"

14.  	"github.com/elastic/go-elasticsearch/v8"
15.  	"github.com/elastic/go-elasticsearch/v8/esapi"
16.  )

18.  func main() {
19.  	cfg := elasticsearch.Config{
20.  		Addresses: []string{
21.  			"https://localhost:9200",
22.  		},
23.  		CertificateFingerprint: "d293735f339412c738c5a258fe950fb2fcdaa33f772365a948abbb332a7c58b4",
24.  		APIKey:                 "dTM5Tm40VUJ2OUFNdUFLUDNnRUU6NWQ2QzhTZmhURk82MUFsUFE0a2ltUQ==",
25.  		RetryOnStatus:          []int{429, 502, 503, 504},
26.  		RetryBackoff: func(i int) time.Duration {
27.  			// A simple exponential delay
28.  			d := time.Duration(math.Exp2(float64(i))) * time.Second
29.  			log.Printf("Attempt: %d | Sleeping for %s...\n", i, d)
30.  			return d
31.  		},
32.  	}

34.  	log.SetFlags(0)

36.  	var (
37.  		r map[string]interface{}
38.  		// wg sync.WaitGroup
39.  	)

41.  	es, err := elasticsearch.NewClient(cfg)
42.  	log.Println(err)
43.  	if err == nil {
44.  		log.Println("Successfully connected to Elasticsearch!")
45.  	}
46.  	// 1. Get cluster info
47.  	//
48.  	res, err := es.Info()
49.  	if err != nil {
50.  		log.Fatalf("Error getting response: %s", err)
51.  	}
52.  	defer res.Body.Close()

54.  	// Check response status
55.  	if res.IsError() {
56.  		log.Fatalf("Error: %s", res.String())
57.  	}

59.  	// Deserialize the response into a map.
60.  	if err := json.NewDecoder(res.Body).Decode(&r); err != nil {
61.  		log.Fatalf("Error parsing the response body: %s", err)
62.  	}

64.  	// Print client and server version numbers.
65.  	log.Printf("Client: %s", elasticsearch.Version)
66.  	log.Printf("Server: %s", r["version"].(map[string]interface{})["number"])
67.  	log.Println(strings.Repeat("~", 37))

69.  	var wg sync.WaitGroup
70.  	for i, title := range []string{"Test One", "Test Two"} {
71.  		wg.Add(1)

73.  		go func(i int, title string) {
74.  			defer wg.Done()

76.  			// Build the request body.
77.  			data, err := json.Marshal(struct{ Title string }{Title: title})
78.  			if err != nil {
79.  				log.Fatalf("Error marshaling document: %s", err)
80.  			}

82.  			// Set up the request object.
83.  			req := esapi.IndexRequest{
84.  				Index:      "test",
85.  				DocumentID: strconv.Itoa(i + 1),
86.  				Body:       bytes.NewReader(data),
87.  				Refresh:    "true",
88.  			}

90.  			// Perform the request with the client.
91.  			res, err := req.Do(context.Background(), es)
92.  			if err != nil {
93.  				log.Fatalf("Error getting response: %s", err)
94.  			}
95.  			defer res.Body.Close()

97.  			if res.IsError() {
98.  				log.Printf("[%s] Error indexing document ID=%d", res.Status(), i+1)
99.  			} else {
100.  				// Deserialize the response into a map.
101.  				var r map[string]interface{}
102.  				if err := json.NewDecoder(res.Body).Decode(&r); err != nil {
103.  					log.Printf("Error parsing the response body: %s", err)
104.  				} else {
105.  					// Print the response status and indexed document version.
106.  					log.Printf("[%s] %s; version=%d", res.Status(), r["result"], int(r["_version"].(float64)))
107.  				}
108.  			}
109.  		}(i, title)
110.  	}
111.  	wg.Wait()
112.  	log.Println(strings.Repeat("-", 37))

114.  	// 3. Search for the indexed documents
115.  	//
116.  	// Build the request body.
117.  	var buf bytes.Buffer
118.  	query := map[string]interface{}{
119.  		"query": map[string]interface{}{
120.  			"match": map[string]interface{}{
121.  				"Title": "test",
122.  			},
123.  		},
124.  	}
125.  	if err := json.NewEncoder(&buf).Encode(query); err != nil {
126.  		log.Fatalf("Error encoding query: %s", err)
127.  	}

129.  	// Perform the search request.
130.  	res, err = es.Search(
131.  		es.Search.WithContext(context.Background()),
132.  		es.Search.WithIndex("test"),
133.  		es.Search.WithBody(&buf),
134.  		es.Search.WithTrackTotalHits(true),
135.  		es.Search.WithPretty(),
136.  	)
137.  	if err != nil {
138.  		log.Fatalf("Error getting response: %s", err)
139.  	}
140.  	defer res.Body.Close()

142.  	if res.IsError() {
143.  		var e map[string]interface{}
144.  		if err := json.NewDecoder(res.Body).Decode(&e); err != nil {
145.  			log.Fatalf("Error parsing the response body: %s", err)
146.  		} else {
147.  			// Print the response status and error information.
148.  			log.Fatalf("[%s] %s: %s",
149.  				res.Status(),
150.  				e["error"].(map[string]interface{})["type"],
151.  				e["error"].(map[string]interface{})["reason"],
152.  			)
153.  		}
154.  	}

156.  	if err := json.NewDecoder(res.Body).Decode(&r); err != nil {
157.  		log.Fatalf("Error parsing the response body: %s", err)
158.  	}
159.  	// Print the response status, number of results, and request duration.
160.  	log.Printf(
161.  		"[%s] %d hits; took: %dms",
162.  		res.Status(),
163.  		int(r["hits"].(map[string]interface{})["total"].(map[string]interface{})["value"].(float64)),
164.  		int(r["took"].(float64)),
165.  	)
166.  	// Print the ID and document source for each hit.
167.  	for _, hit := range r["hits"].(map[string]interface{})["hits"].([]interface{}) {
168.  		log.Printf(" * ID=%s, %s", hit.(map[string]interface{})["_id"], hit.(map[string]interface{})["_source"])
169.  	}

171.  	log.Println(strings.Repeat("=", 37))
172.  }

`![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
复制代码

关于 7.x 的文章:Elasticsearch:Elasticsearch 开发入门 - Golang

更多例子:go-elasticsearch/_examples at main · elastic/go-elasticsearch · GitHub

分类:
后端
标签:
收藏成功!
已添加到「」, 点击更改