一、ES介绍
1、ES定义
ES是elaticsearch简写, Elasticsearch是一个开源的高扩展的分布式全文检索引擎,它可以近乎实时的存储、检索数据;本身扩展性很好,可以扩展到上百台服务器,处理PB级别的数据。 Elasticsearch也使用Java开发并使用Lucene作为其核心来实现所有索引和搜索的功能,但是它的目的是通过简单的RESTful API来隐藏Lucene的复杂性,从而让全文搜索变得简单。
2、ES的使用场景
- 为用户提供按关键字查询的全文搜索功能
- 实现企业海量数据的处理分析的解决方案。大数据领域的重要一份子,如著名的ELK框架(ElasticSearch,Logstash,Kibana)
3、ES工作原理
当ElasticSearch的节点启动后,它会利用多播(multicast)(或者单播,如果用户更改了配置)寻找集群中的其它节点,并与之建立连接。这个过程如下图所示:
4、ES核心概念
- Cluster:集群,ES可以作为一个独立的单个搜索服务器。不过,为了处理大型数据集,实现容错和高可用性,ES可以运行在许多互相合作的服务器上。这些服务器的集合称为集群。
- Node:节点,形成集群的每个服务器称为节点。
- Shard:分片,当有大量的文档时,由于内存的限制、磁盘处理能力不足、无法足够快的响应客户端的请求等,一个节点可能不够。这种情况下,数据可以分为较小的分片。每个分片放到不同的服务器上。 当你查询的索引分布在多个分片上时,ES会把查询发送给每个相关的分片,并将结果组合在一起,而应用程序并不知道分片的存在。即:这个过程对用户来说是透明的。
- Replia:副本,为提高查询吞吐量或实现高可用性,可以使用分片副本。 副本是一个分片的精确复制,每个分片可以有零个或多个副本。ES中可以有许多相同的分片,其中之一被选择更改索引操作,这种特殊的分片称为主分片。 当主分片丢失时,如:该分片所在的数据不可用时,集群将副本提升为新的主分片。
- 全文检索:全文检索就是对一篇文章进行索引,可以根据关键字搜索,类似于mysql里的like语句,全文索引就是把内容根据词的意义进行分词,然后分别创建索引,例如”你们的激情是因为什么事情来的” 可能会被分词成:“你们“,”激情“,“什么事情“,”来“ 等token,这样当你搜索“你们” 或者 “激情” 都会把这句搜出来。
5、ES特点和优势
- 分布式实时文件存储,可将每一个字段存入索引,使其可以被检索到。
- 实时分析的分布式搜索引擎。 分布式:索引分拆成多个分片,每个分片可有零个或多个副本。集群中的每个数据节点都可承载一个或多个分片,并且协调和处理各种操作; 负载再平衡和路由在大多数情况下自动完成。
- 可以扩展到上百台服务器,处理PB级别的结构化或非结构化数据。也可以运行在单台PC上
- 支持插件机制,分词插件、同步插件、Hadoop插件、可视化插件等。
二、ES安装(Windows端)
1、安装JDK环境与环境变量配置
因为ElasticSearch是用Java语言编写的,所以必须安装JDK的环境,并且是JDK 1.8以上
2、官网下载,选择windows端
3、解压压缩包至合适目录
4、修改配置文件
打开config/elasticsearch.yml文件,修改里面的 xpack.security.enabled: true -> xpack.security.enabled: false
另外es配置默认端口号为:9200,如果需要修改也可以在上面配置文件中修改
5、启动ES
切换到bin目录下,双击 elasticsearch.bat 即可运行ES
6、查看运行状态
使用浏览器打开 http://127.0.0.1:9200/ 地址查看运行状态,出现如下内容表示成功
三、Go 连接和使用ES
1、安装es驱动
go get github.com/olivere/elastic/v7
2、创建客户端
在开始之前,我们需要创建一个 Elasticsearch 客户端实例。需要初始化客户端连接,然后去获取相关节点信息,之后会输出一个elasticsearch结构体
package main
import (
"context"
"fmt"
"github.com/olivere/elastic/v7"
"log"
)
func main() {
// 初始化es客户端
client, err := elastic.NewClient(
elastic.SetURL("http://localhost:9200"),
elastic.SetSniff(false),
)
if err != nil {
log.Fatal("ES连接错误: ", err)
}
info, err := client.NodesInfo().Do(context.Background())
if err != nil {
log.Fatal("获取节点信息错误: ", err)
}
fmt.Println(info)
fmt.Println("ES连接成功")
}
2、使用 ES API
ES 提供了一系列的 API 来操作 ES 集群中的数据,包括索引操作、搜索、聚合等。在使用 API 时,需要注意相应的请求方法和请求路径、请求体等细节。
(1)创建索引文档
在createIndex函数中,我们使用client.CreateIndex(indexName).Do(ctx)来创建索引。如果创建索引失败,将返回错误信息。
在addDocument函数中,我们使用client.Index().Index(indexName).Id(doc.ID).BodyJson(doc).Do(ctx)来添加文档。它指定了要添加到的索引、文档的ID和内容。如果添加文档失败,将返回错误信息。
package main
import (
"context"
"fmt"
"github.com/olivere/elastic/v7"
"log"
)
type Document struct {
ID string `json:"id"`
Name string `json:"name"`
}
func createIndex(ctx context.Context, client *elastic.Client, indexName string) error {
_, err := client.CreateIndex(indexName).Do(ctx)
if err != nil {
return fmt.Errorf("error creating the index: %s", err)
}
return nil
}
func addDocument(ctx context.Context, client *elastic.Client, indexName string, doc Document) error {
_, err := client.Index().Index(indexName).Id(doc.ID).BodyJson(doc).Do(ctx)
if err != nil {
return fmt.Errorf("error indexing document: %s", err)
}
return nil
}
func main() {
// 初始化es客户端
client, err := elastic.NewClient(
elastic.SetURL("http://localhost:9200"),
elastic.SetSniff(false),
)
if err != nil {
log.Fatal("ES连接错误: ", err)
}
fmt.Println("ES连接成功")
indexName := "my_index"
ctx := context.Background()
// 创建索引
err = createIndex(ctx, client, indexName)
if err != nil {
log.Fatalf("Error creating the index: %s", err)
} else {
log.Printf("Index created: %s", indexName)
}
// 添加文档
doc := Document{
ID: "1",
Name: "测试test",
}
err = addDocument(ctx, client, indexName, doc)
if err != nil {
log.Fatalf("Error indexing document: %s", err)
} else {
log.Printf("Document indexed: %s", doc.ID)
}
}
运行结果
(2)搜索文档
在searchDocuments函数中,我们使用client.Search()创建一个搜索请求,并设置要搜索的索引名称和搜索条件(例如,使用elastic.NewMatchQuery进行简单的字段匹配)
package main
import (
"context"
"encoding/json"
"fmt"
"github.com/olivere/elastic/v7"
"log"
)
// 省略代码
// 搜索文档
func searchDocuments(ctx context.Context, client *elastic.Client, indexName string, searchTerm string) (*elastic.SearchResult, error) {
searchResult, err := client.Search().
Index(indexName).
Query(elastic.NewMatchQuery("name", searchTerm)).
Do(ctx)
if err != nil {
return nil, fmt.Errorf("error searching documents: %s", err)
}
return searchResult, nil
}
func main() {
// 省略上面代码
// 搜索文档
searchTerm := "测试"
results, err := searchDocuments(ctx, client, indexName, searchTerm)
if err != nil {
log.Fatalf("Error searching documents: %s", err)
} else {
log.Printf("Search results for '%s':", searchTerm)
for _, hit := range results.Hits.Hits {
var doc Document
err := json.Unmarshal(hit.Source, &doc)
if err != nil {
log.Printf("Error unmarshaling document: %s", err)
} else {
log.Printf("ID: %s, Name: %s", doc.ID, doc.Name)
}
}
}
}
运行结果
(3)更新文档
// 更新文档
func updateDocument(ctx context.Context, client *elastic.Client, index string, docID string, updatedData map[string]interface{}) error {
updateResult, err := client.Update().
Index(index).
Id(docID).
Doc(updatedData).
Do(ctx)
if err != nil {
return fmt.Errorf("更新文档时发生错误:%v", err)
}
if updateResult.Result != "updated" {
return fmt.Errorf("文档未被更新:%s", updateResult.Result)
}
return nil
}
func main() {
// 省略上面代码
// 更新文档
docID := "1"
updatedData := map[string]interface{}{
"name": "张三",
}
err = updateDocument(context.Background(), client, indexName, docID, updatedData)
if err != nil {
fmt.Println("更新文档失败:", err)
return
}
fmt.Println("文档已成功更新")
}
运行结果
(4)删除文档
在下面函数中,我们使用 client.Delete().Index(indexName).Id(docID).Do(ctx) 来构建并执行删除请求。然后,我们检查删除结果是否成功,以及返回结果是否为 "deleted"。如果删除过程中出现错误,函数将返回带有错误信息的错误对象。如果文档未被删除,则函数返回一个表示删除失败的错误。
// 删除文档
func deleteDocument(ctx context.Context, client *elastic.Client, indexName string, docID string) error {
deleteResult, err := client.Delete().
Index(indexName).
Id(docID).
Do(ctx)
if err != nil {
return fmt.Errorf("删除文档时发生错误:%s", err)
}
if deleteResult.Result != "deleted" {
return errors.New("文档未被删除")
}
return nil
}
func main() {
// 省略上面代码
// 删除文档
docID := "1"
err = deleteDocument(ctx, client, indexName, docID)
if err != nil {
fmt.Println("删除文档失败:", err)
return
}
fmt.Println("文档已成功删除")
}
运行结果