es的核心概念:
①索引: 索引是一种类似于数据库的数据存储方式,它包含了一组具有相似结构的文档。每个文档都属于一个索引,而索引由一个或多个分片组成,这些分片分布在集群的不同节点上。
②文档: 文档是 Elasticsearch 存储的基本数据单元。它是一条 JSON 格式的数据记录,可以包含不同的字段,例如文本、数字、日期等。每个文档都有一个唯一的标识,称为文档 ID。
③类型: 在早期版本的 Elasticsearch 中,一个索引可以包含多个类型,每个类型代表不同的数据结构。然而,从 Elasticsearch 7.0 开始,类型已经被逐步废弃,一个索引只能包含一个类型。
④分片: 分片是将索引数据划分为更小的单元,使其能够分布在集群中不同的节点上存储。每个分片都是一个独立的索引,包含了一部分文档数据。分片的使用使得 Elasticsearch 能够水平扩展,处理大规模数据。
⑤副本: 副本是分片的复制品,用于提高数据的可用性和容错性。每个分片可以有多个副本,副本会分布在不同的节点上,当主分片不可用时,副本会自动接管服务。
⑥倒排索引: 也叫反向索引。Elasticsearch 使用倒排索引来加快文本搜索。倒排索引记录了每个词在哪些文档中出现,而不是记录每个文档中包含了哪些词。
⑦分析器: 分析器用于将文本数据拆分成一系列的词条,并进行词条的标准化、过滤和转换。分析器是文本搜索和索引的重要组成部分。
详细可以参考知乎文章:终于有人把Elasticsearch原理讲透了! - 知乎 (zhihu.com)很通俗易懂!
其中分词以及倒排索引是es最重要的核心组成部分!【注意:keyword是不会进行分词的,一般都text进行分词建立倒排索引的】
索引引擎三大过程是:爬取内容、进行分词、建立倒排索引
明白es是对搜索引擎的操作都封装成Restful的api,通过http的请求就能对其进行操作的了!同时考虑了海量的数据,实现分布式,是一个可以存储海量数据的分布式搜索引擎!
es是会对数据进行切分的,同时一个分片会保存多个副本,保证在分布式的环境下高可用!绿色表示数据块,其中es中数据库也是备份存储至多个节点中的类似于master-slave的架构,在 Elasticsearch 中,节点是对等的,节点间会通过自己的一些规则选取集群的 Master,Master 会负责集群状态信息的改变,并同步给其他节点。
如果建立一个索引,就会想通知master建立索引,然后这个信息再同步给其他的节点!注意只有建立索引和类型需要经过master,数据的写入有一个简单的 Routing 规则,可以 Route 到集群中的任意节点,所以数据写入压力是分散在整个集群的。
拉去es的镜像:
docker pull elasticsearch:7.12.0
配置文件:
在window中目录配置一个elasticsearch.yml写入文件
http.host:0.0.0.0
启动容器:
docker run --name es -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" -e ES_JAVA_OPTS="-Xms84m -Xmx512m" -v E:\\docker_win\\es\\config\\elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml -v E:\\docker_win\\es\\data:/usr/share/elasticsearch/data -v E:\\docker_win\\es\\plugins:/usr/share/elasticsearch/plugins -d elasticsearch:7.12.0
访问127.0.0.1:9200就可以看到
安装ElasticHD插件
https://github.com/qax-os/ElasticHD/releases
es链接:
package conc
import (
"es_study/global"
"fmt"
"github.com/olivere/elastic/v7"
)
func EsConnect() {
// 链接es 参数二关闭ip检测 参数三 账号和密码
client, err := elastic.NewClient(elastic.SetURL("http://localhost:9200"),
elastic.SetSniff(false),
elastic.SetBasicAuth("", ""))
if err != nil {
fmt.Println(err)
return
}
// 获取连接成功
global.ESClient = client
fmt.Println(client)
}
es认证
我们不使用认证的情况是:
①服务器自己使用,9200,9300端口不对外开放
②本身跑在127.0.0.1上的
需要认证的是:
①es需要对外提供服务的
参考文章:https://blog.csdn.net/qq_38669698/article/details/130529829
去elasticsearch.yml配置文件
http.cors.enabled: true
http.cors.allow-origin: "*"
http.cors.allow-headers: Authorization
xpack.security.enabled: true
xpack.security.transport.ssl.enabled: true
保存后重启ES
docker restart es
进入ES容器
docker exec -it es /bin/bash
执行命令设置密码;
./bin/elasticsearch-setup-passwords interactive
索引操作:
创建索引!第一次创建索引的时候可以成功!第二次的因为存在了 会报错了!
package indexs
import (
"context"
"es_study/global"
"es_study/models"
"fmt"
)
func CreateIndex() {
// 第一个参数是索引名字
createIndex, err := global.ESClient.CreateIndex("user_index").BodyString(models.UserModel{}.Mapping()).Do(context.Background())
if err != nil {
fmt.Println(err)
return
}
fmt.Println(createIndex, "索引创建成功!")
}
判断索引是否存在:
// ExistsIndex 判断索引是否存在
func ExistsIndex(index string) bool {
exists, _ := global.ESClient.IndexExists(index).Do(context.Background())
return exists
}
删除索引:
func DeleteIndex(index string) {
_, err := global.ESClient.
DeleteIndex(index).Do(context.Background())
if err != nil {
fmt.Println(err)
return
}
fmt.Println(index, "索引删除成功")
}
添加文档:
func DocCreate() {
user := models.UserModel{
ID: 12,
UserName: "lisi",
Age: 23,
NickName: "夜空中最亮的niuzai",
CreatedAt: time.Now().Format("2006-01-02 15:04:05"),
Title: "今天天气很不错",
}
indexResponse, err := global.ESClient.Index().Index(user.Index()).BodyJson(user).Do(context.Background())
if err != nil {
fmt.Println(err)
return
}
fmt.Printf("%#v\n", indexResponse)
}
注意:如果我们添加的文档中mapping中没有的字段,那么es会自动创建这个字段对应的mapping
批量添加:
func DocCreateBatch() {
list := []models.UserModel{
{
ID: 12,
UserName: "niuzai",
NickName: "夜空中最亮的牛牛",
CreatedAt: time.Now().Format("2006-01-02 15:04:05"),
},
{
ID: 13,
UserName: "lisa",
NickName: "夜空中最亮的牛仔",
CreatedAt: time.Now().Format("2006-01-02 15:04:05"),
},
}
bulk := global.ESClient.Bulk().Index(models.UserModel{}.Index()).Refresh("true")
for _, model := range list {
req := elastic.NewBulkCreateRequest().Doc(model)
bulk.Add(req)
}
res, err := bulk.Do(context.Background())
if err != nil {
fmt.Println(err)
return
}
fmt.Println(res.Succeeded())
}
删除文档
首相可以根据id进行删除
func DocDelete() {
deleteResponse, err := global.ESClient.Delete().
Index(models.UserModel{}.Index()).Id("tmcqfYkBWS69Op6Q4Z0t").Refresh("true").Do(context.Background())
if err != nil {
fmt.Println(err)
return
}
fmt.Println(deleteResponse)
}
如果文档不存在的哇,会报404的错!
根据id批量删除
func DocDeleteBatch() {
idList := []string{
"tGcofYkBWS69Op6QHJ2g",
"tWcpfYkBWS69Op6Q050w",
}
bulk := global.ESClient.Bulk().Index(models.UserModel{}.Index()).Refresh("true")
for _, s := range idList {
req := elastic.NewBulkDeleteRequest().Id(s)
bulk.Add(req)
}
res, err := bulk.Do(context.Background())
if err != nil {
fmt.Println(err)
return
}
fmt.Println(res.Succeeded()) // 实际删除的文档切片
}
文档查询:
func DocFind() {
limit := 2
page := 4
from := (page - 1) * limit
query := elastic.NewBoolQuery()
// Form的第几条开始查 size是查几条
res, err := global.ESClient.Search(models.UserModel{}.Index()).Query(query).From(from).Size(limit).Do(context.Background())
if err != nil {
fmt.Println(err)
return
}
count := res.Hits.TotalHits.Value // 总数
fmt.Println(count)
for _, hit := range res.Hits.Hits {
fmt.Println(string(hit.Source))
}
}
精确匹配:
query := elastic.NewTermQuery("user_name", "niuzai")
注意:只能针对的是keyword的查询
模糊查询:
主要是查text,也能查keyword
模糊匹配keyword字段,是需要查完整的
匹配text字段则不用,搜完整的也会搜出很多【因为会分词匹配了,故匹配到的都会查询出来】
query := elastic.NewMatchQuery("nick_name", "夜空中最亮的枫枫")
嵌套字段的搜索:
"title": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
因为title是text类型,只能模糊匹配,但是需要精确匹配的时候,也能通过title.keyword的形式进行精确匹配
query := elastic.NewTermQuery("title.keyword", "这是我的niuzai") // 精确匹配
//query := elastic.NewMatchQuery("title", "这是我的niuzai") // 模糊匹配
文档更新:
func DocUpdate() {
res, err := global.ESClient.Update().Index(models.UserModel{}.Index()).
Id("vmdnfYkBWS69Op6QEp2Y").Doc(map[string]any{
"user_name": "你好niuzai",
}).Do(context.Background())
if err != nil {
fmt.Println(err)
return
}
fmt.Printf("%#v\n", res)
}
tchQuery(“title”, “这是我的niuzai”) // 模糊匹配
### 文档更新:
```go
func DocUpdate() {
res, err := global.ESClient.Update().Index(models.UserModel{}.Index()).
Id("vmdnfYkBWS69Op6QEp2Y").Doc(map[string]any{
"user_name": "你好niuzai",
}).Do(context.Background())
if err != nil {
fmt.Println(err)
return
}
fmt.Printf("%#v\n", res)
}