一、了解elastic
Elasticsearch(中文名:弹性搜索)是一个开源的分布式搜索和分析引擎,它专注于处理大规模数据的存储、检索和分析。以下是 Elasticsearch 的一些关键特点和用途:
-
全文搜索引擎:Elasticsearch 可以存储和搜索大量文本数据,以支持高效的全文搜索。它使用倒排索引技术,允许快速检索文档中的关键词和短语。
-
分布式和高可用性:Elasticsearch 具有分布式架构,可以在多个节点上复制数据,确保高可用性和容错性。如果一个节点故障,系统仍然可以继续工作。
-
实时性:Elasticsearch 具有近实时的索引更新能力,适用于需要快速反馈和查询最新数据的应用场景。
-
大数据分析:Elasticsearch 不仅用于搜索,还可以用于大数据分析。它与 Logstash 和 Kibana 一起构成了 ELK 堆栈,用于处理、分析和可视化大规模数据日志。
-
多种查询支持:Elasticsearch 提供了丰富的查询DSL(领域特定语言),包括全文搜索、过滤、聚合等多种查询类型,以满足不同的搜索和分析需求。
-
开源和社区支持:Elasticsearch 是开源项目,拥有庞大的社区支持,可以轻松地找到文档、教程和插件来扩展其功能。
-
可扩展性:Elasticsearch 可以水平扩展,通过添加更多的节点来处理更多的数据和查询负载,因此非常适合应对不断增长的数据量。
调用示例:
func main() {
// 初始化Elastic 查询库
InitElastic()
// 索引名
indexName := "test_tiktok"
// 创建索引
fieldArr := map[string]string{
"id": "int",
"title": "text",
}
b := Create(indexName, fieldArr)
fmt.Println("b =========== ", b)
// 新增数据
var strArr = []string{"aa", "bb", "cc", "dd", "ee", "ff,aa"}
for k, v := range strArr {
id := strconv.Itoa(k + 1)
add := AddIndexData(indexName, id, map[string]interface{}{
"id": k,
"title": v,
})
fmt.Println("add ============ ", add)
}
// 查询
query_data := Query("aa", indexName, []string{"title"}, []string{"title"})
type QueryData struct {
List []struct {
Id int `json:"id"`
Title string `json:"title"`
} `json:"list"`
Total int `json:"total"`
}
qd := QueryData{}
err := json.Unmarshal(query_data, &qd)
fmt.Println("err ====== ", err)
fmt.Printf("a ====== %+v\\n ", qd)
// 删除
b = DelIndexData(indexName, "1")
fmt.Println("delete ====== ", b)
}
使用步骤
1.引入库
github.com/elastic/go-elasticsearch/v6
github.com/elastic/go-elasticsearch/v6/esapi
2.初始化,获取elastic实例
代码如下(示例):
// 初始化索引库
var Elastic *elasticsearch.Client
var ElasticConfig = elasticsearch.Config{
Addresses: []string{"http://127.0.0.1:9200"}, // 公网地址
Username: "username", // 用户名
Password: "password", // 密码
}
func InitElastic() {
var r map[string]interface{}
var err error
// 创建es连接
Elastic, err = elasticsearch.NewClient(ElasticConfig)
if err != nil {
log.Fatalf("Error creating the client: %s", err)
}
// 1. 获取集群信息
res, err := Elastic.Info()
if err != nil {
log.Fatalf("Error getting response: %s", err)
}
// 检查响应状态
if res.IsError() {
log.Fatalf("Error: %s", res.String())
}
if err = json.NewDecoder(res.Body).Decode(&r); err != nil {
log.Fatalf("Error parsing the response body: %s", err)
}
// 打印信息
fmt.Printf("Client: %s \n", elasticsearch.Version)
fmt.Printf("Server: %s \n", r["version"].(map[string]interface{})["number"])
fmt.Println(strings.Repeat("~", 37))
}
3、创建索引容器
/**
创建索引容器
indexName:索引名
fieldArr:字段信息数组
返回值:bool
*/
func Create(indexName string, fieldArr map[string]string) bool {
// 查询索引是否存在
res_isset, err_isset := Elastic.Indices.Get([]string{indexName})
if err_isset != nil {
log.Fatalf("Error creating index: %s", err_isset)
return false
}
if res_isset.StatusCode == 404 {
// 创建索引
createIndexRequest := fmt.Sprintf(`{
"settings": {
"number_of_shards": 5,
"number_of_replicas": 1
}
}`)
// 发送创建索引的请求
res, err := Elastic.Indices.Create(indexName, Elastic.Indices.Create.WithBody(strings.NewReader(createIndexRequest)))
if err != nil || res.StatusCode != 200 {
log.Fatalf("Error creating index: %s", err)
return false
}
defer res.Body.Close()
}
// 创建映射
var mappings = map[string]interface{}{}
properties := map[string]interface{}{}
for k, v := range fieldArr {
if v == "int" {
properties[k] = map[string]interface{}{
"type": "integer",
}
} else if v == "text" {
properties[k] = map[string]interface{}{
"type": "text",
"analyzer": "ik_max_word", // 使用 ik 分词器
}
}
mappings["properties"] = properties
}
data, _ := json.Marshal(mappings)
req := esapi.IndexRequest{
Index: indexName,
DocumentID: "user",
DocumentType: "_mappings",
Body: strings.NewReader(string(data)),
}
// 执行创建映射
res, err := req.Do(context.Background(), Elastic)
if err != nil || res.StatusCode != 200 {
fmt.Printf("Error getting response: %s \n", err)
return false
}
defer res.Body.Close()
return true
}
创建索引的配置参数解读:
number_of_shards:每个索引的主分片数,默认值是5。这个配置在索引创建后不能修改
number_of_replicas:每个主分片的副本数,默认值是1。对于活动的索引库,这个值可以随时修改。
映射字段类型说明
上述例子列举了2种类型的字段创建方式,其余可根据自身需求更改
▶ 字符串
● text
● keyword
▶ 数字
● long
● integer
● short
● byte
● double
● float
● half_float
● scaled_float
▶ 布尔类型
● boolean
▶ 日期
● date
4、添加 更新索引数据
/**
新增 / 更新 数据
indexName:索引名
id:唯一值
info:数据内容
*/
func AddIndexData(indexName, id string, info interface{}) bool {
data, _ := json.Marshal(info)
// Set up the request object.
req := esapi.IndexRequest{
Index: indexName,
DocumentID: id,
Body: strings.NewReader(string(data)),
Refresh: "true",
}
// Perform the request with the client.
res, err := req.Do(context.Background(), Elastic)
//200 或 201都是成功
// 200 更新成功
// 201 新增成功
if err != nil || (res.StatusCode != 200 && res.StatusCode != 201) {
fmt.Printf("Error getting response: %s \n", err)
return false
}
defer res.Body.Close()
if res.IsError() {
fmt.Printf("[%s] Error indexing document ID=%s \n", res.Status(), fmt.Sprintf("%s", id))
return false
} else {
// Deserialize the response into a map.
var r map[string]interface{}
if err := json.NewDecoder(res.Body).Decode(&r); err != nil {
fmt.Printf("Error parsing the response body: %s \n", err)
return false
} else {
// Print the response status and indexed document version.
fmt.Printf("[%s] %s; version=%d \n", res.Status(), r["result"], int(r["_version"].(float64)))
return true
}
}
return true
}
5、查询
/**
查询数据
keyword:关键字
indexName:索引名
field:需要匹配的字段
highlightField:需要高亮显示的字段,如果不需要传入空数组即可
*/
func Query(keyword, indexName string, field, highlightField []string) []byte {
log.SetFlags(0)
var es = Elastic
var buf bytes.Buffer
// 查询
query := map[string]interface{}{
"query": map[string]interface{}{
"multi_match": map[string]interface{}{
"query": keyword, // 需要查询匹配的关键字
"fields": field, // 需要匹配的字段
},
},
}
// 查询结果高亮显示,高亮标签可自定义
if len(highlightField) > 0 {
var highlight = map[string]interface{}{
"pre_tags": []string{"<span class='search_highlight'>"},
"post_tags": []string{"</span>"},
}
var fields = map[string]interface{}{}
for _, v := range highlightField {
fields[v] = map[string]interface{}{
"number_of_fragments": 0,
}
}
highlight["fields"] = fields
query["highlight"] = highlight
}
if err := json.NewEncoder(&buf).Encode(query); err != nil {
return []byte{}
}
// 发起查询请求
res, err := es.Search(
es.Search.WithContext(context.Background()),
es.Search.WithIndex(indexName),
es.Search.WithBody(&buf),
es.Search.WithTrackTotalHits(true),
es.Search.WithPretty(),
)
if err != nil {
return []byte{}
}
defer res.Body.Close()
if res.IsError() {
var re map[string]interface{}
if err := json.NewDecoder(res.Body).Decode(&re); err != nil {
return []byte{}
}
}
var rData ReturnData
if err := json.NewDecoder(res.Body).Decode(&rData); err != nil {
return []byte{}
}
// 组合返回数据
list := []map[string]interface{}{}
for _, v := range rData.Hits.Hits {
list = append(list, v.Source)
}
fmt.Println(strings.Repeat("=", 37))
total := len(list)
jsonData, _ := json.Marshal(map[string]interface{}{
"total": total,
"list": list,
})
return jsonData
}
6、删除数据
/**
删除数据
indexName:索引库名
id:需要删除的数据id
*/
func DelIndexData(indexName, id string) bool {
res, err := Elastic.Delete(indexName, id)
if err != nil || res.StatusCode != 200 {
fmt.Printf("Error getting response: %s \n", err)
return false
}
defer res.Body.Close()
if res.IsError() {
var re map[string]interface{}
if err := json.NewDecoder(res.Body).Decode(&re); err != nil {
fmt.Printf("Error parsing the response body: %s \n", err)
} else {
// Print the response status and error information.
fmt.Printf("[%s] \n",
res.Status(),
)
}
return false
}
var rData map[string]interface{}
if err := json.NewDecoder(res.Body).Decode(&rData); err != nil {
fmt.Printf("Error parsing the response body: %s \n", err)
return false
}
fmt.Println("删除成功")
return true
}
总结
总之,Elasticsearch 是一个多功能的搜索和分析引擎,适用于各种应用程序,从全文搜索到大数据分析和实时监控等不同领域。它的强大性能和丰富的功能使其成为许多企业和开发者的首选工具之一。
官方文档地址:https://www.elastic.co/guide/en/elasticsearch/reference/6.0/docs-index_.html
可参考博客:https://www.cnblogs.com/mtjb1dd/p/17312804.html、https://zhuanlan.zhihu.com/p/654334870、https://blog.csdn.net/Peanut_508/article/details/116530902、https://blog.csdn.net/qq_29864131/article/details/125865845