初探golang和应用其框架 gin 的 使用教程(四)使用ES(elasticsearch)数据

目录

 

一、引入sdk并封装

1、连接初始化

2、封装可调用函数并使用


说明:本节主要说明基于gin 项目的包管理 go modules。

操作环境:linux 64

golang 版本: 1.13.1

如果有相关需要可联系我本人(联系方式可参照置顶文章 :获取我的联系方式)获取。


一个项目的数据来源有很多,比如mysql,mongdb,redis,ES(elasticsearch)。本节先介绍一下如何使用elasticsearch 里的数据。

 

一、引入sdk并封装

在golang 中,关于elasticsearch 的第三方包主要有两个:

  • https://github.com/olivere/elastic 第三方开发,各个版本都有对应的sdk,文档也丰富
  • https://github.com/elastic/go-elasticsearch

在这我使用的是第一个,根据服务器的es版本选择olivere/elastic版本。

查看es 版本:

curl -XGET http://自己的服务域名或ip:9200

结果如下,es的版本就是version 里的 number 的值,当前使用的是 6.3.2,对应olivere/elastic 的v6版。https://gopkg.in/olivere/elastic.v6。

{
  "name" : "n1",
  "cluster_name" : "xxxxxxxx",
  "cluster_uuid" : "UnMb3NxxxxxxxxxNfoHziw",
  "version" : {
    "number" : "6.3.2",
    "build_flavor" : "default",
    "build_type" : "tar",
    "build_hash" : "053779d",
    "build_date" : "2018-07-20T05:20:23.451332Z",
    "build_snapshot" : false,
    "lucene_version" : "7.3.1",
    "minimum_wire_compatibility_version" : "5.6.0",
    "minimum_index_compatibility_version" : "5.0.0"
  },
  "tagline" : "You Know, for Search"

在 文件中 引入包


import (

	····

	"gopkg.in/olivere/elastic.v6"

	·····

)

在此我封装了一个连接服务。放在 文件夹 connections 中。目录可参照 初探golang和应用其框架 gin 的 使用教程(二)项目布局篇 

初探golang和应用其框架 gin 的 使用教程(四)使用ES(elasticsearch)数据_第1张图片

elastic.go 配置如下:

/**
 * @Author: sunct
 * @Description:
 * @File:  elastic.go
 * @Version: 1.0.0
 * @Date: 2019-10-21 18:06
 */
package connections

import (
	"context"
	"fmt"
	"globaladmin/conf"
	"gopkg.in/olivere/elastic.v6"
	"log"
	"os"
	"strconv"
	"time"
)

type EsClientType struct {
	EsCon *elastic.Client
}
var Timeout="1s" //超时时间
var EsClient EsClientType //连接类型

var host = conf.GetEnv().EsearchServer  //这个是es服务地址,我的是配置到配置文件中了,测试的时候可以写死 比如 http://127.0.0.1:9200



//下面定义的是 聚合时候用的一些参数

type Aggregations struct {
	AVG_Metric AVG_Metric `json:"AVG_Metric"`
}

type AVG_Metric struct {
	Buckets []Metric `json:"buckets"`
}

type Metric struct {
	Avg_time   Value `json:"avg_time"`
}

type Value struct {
	Value float64 `json:"value"`
}

func  init(){
	elastic.SetSniff(false) //必须 关闭 Sniffing
	//es 配置
	var err error
	//EsClient.EsCon, err = elastic.NewClient(elastic.SetURL(host))
	EsClient.EsCon, err =elastic.NewClient(
		elastic.SetURL(host),
		elastic.SetSniff(false),
		elastic.SetHealthcheckInterval(10*time.Second),
		elastic.SetGzip(true),
		elastic.SetErrorLog(log.New(os.Stderr, "ELASTIC ", log.LstdFlags)),
		elastic.SetInfoLog(log.New(os.Stdout, "", log.LstdFlags)),
	)
	if err != nil {
		panic(err)
	}
	info, code, err := EsClient.EsCon.Ping(host).Do(context.Background())
	if err != nil {
		panic(err)
	}

	fmt.Printf("Elasticsearch returned with code %d and version %s\n", code, info.Version.Number)

	esversion, err := EsClient.EsCon.ElasticsearchVersion(host)
	if err != nil {
		panic(err)
	}
	fmt.Printf("Elasticsearch version %s\n", esversion)
	fmt.Println("conn es succ",EsClient.EsCon)
}



//创建
func(client *EsClientType) Create(Params map[string]string) string {
	//使用字符串
	var res  *elastic.IndexResponse
	var err  error

	res,err = client.EsCon.Index().
		    Index(Params["index"]).
			Type(Params["type"]).
			Id(Params["id"]).BodyJson(Params["bodyJson"]).
			Do(context.Background())

	if err != nil {
		panic(err)
	}
	return res.Result

}

//删除
func (client *EsClientType) Delete(Params map[string]string)string {
	var res  *elastic.DeleteResponse
	var err  error

	res, err = client.EsCon.Delete().Index(Params["index"]).
		Type(Params["type"]).
		Id(Params["id"]).
		Do(context.Background())

	if err != nil {
		println(err.Error())
	}

	fmt.Printf("delete result %s\n", res.Result)
	return res.Result
}

//修改
func (client *EsClientType) Update(Params map[string]string)string {
	var res  *elastic.UpdateResponse
	var err  error

	res, err = client.EsCon.Update().
		Index(Params["index"]).
		Type(Params["type"]).
		Id(Params["id"]).
		Doc(Params["doc"]).
		Do(context.Background())

	if err != nil {
		println(err.Error())
	}
	fmt.Printf("update age %s\n", res.Result)
	return res.Result

}

//查找
func (client *EsClientType) Gets(Params map[string]string) *elastic.GetResult  {
	//通过id查找
	var get1  *elastic.GetResult
	var err  error
	if len(Params["id"])< 0 {
		fmt.Printf("param error")
		return get1
	}

	get1, err = client.EsCon.Get().Index(Params["index"]).Type(Params["type"]).Id(Params["id"]).Do(context.Background())

	if err != nil {
		panic(err)
	}

	return  get1
}

//搜索
func (client EsClientType) Query(Params map[string]string) *elastic.SearchResult {
	var res *elastic.SearchResult
	var err error
	//取所有
	res, err = client.EsCon.Search(Params["index"]).Type(Params["type"]).Do(context.Background())
	if len(Params["queryString"]) > 0 {
		//字段相等
		q := elastic.NewQueryStringQuery(Params["queryString"])
		res, err = client.EsCon.Search(Params["index"]).Type(Params["type"]).Query(q).Do(context.Background())
	}
	if err != nil {
		println(err.Error())
	}

	//if res.Hits.TotalHits > 0 {
	//	fmt.Printf("Found a total of %d Employee \n", res.Hits.TotalHits)
	//}
	return res
}


//简单分页 可用

func (client *EsClientType) List(Params map[string]string) *elastic.SearchResult {
	var res *elastic.SearchResult
	var err error
	size, _ := strconv.Atoi(Params["size"])
	page, _ := strconv.Atoi(Params["page"])
	q := elastic.NewQueryStringQuery(Params["queryString"])

	//排序类型 desc asc es 中只使用 bool 值  true or false
	sort_type :=true
	if Params["sort_type"]=="desc" {
		sort_type =false
	}
	//fmt.Printf(" sort info  %s,%s\n", Params["sort"],Params["sort_type"])
	if size < 0 || page < 0 {
		fmt.Printf("param error")
		return res
	}
	if len(Params["queryString"]) >0 {
		res,err = client.EsCon.Search(Params["index"]).
			Type(Params["type"]).
			Query(q).
			Size(size).
			From((page)*size).
			Sort(Params["sort"],sort_type).
			Timeout(Timeout).
			Do(context.Background())

	}else{
		res,err = client.EsCon.Search(Params["index"]).
			Type(Params["type"]).
			Size(size).
			From((page)*size).
			Sort(Params["sort"],sort_type).
			//SortBy(elastic.NewFieldSort("add_time").UnmappedType("long").Desc(), elastic.NewScoreSort()).
			Timeout(Timeout).
			Do(context.Background())
	}

	if err != nil {
		println("func list error:"+err.Error())
	}
	return res

}
//聚合 平均 可用
func (client *EsClientType) Aggregation(Params map[string]string) *elastic.SearchResult {
	var res *elastic.SearchResult
	var err error

	//需要聚合的指标 求平均
	avg := elastic.NewAvgAggregation().Field(Params["avg"])
	//单位时间和指定字段
	aggs := elastic.NewDateHistogramAggregation().
		Interval("day").
		Field(Params["field"]).
		//TimeZone("Asia/Shanghai").
		SubAggregation(Params["agg_name"], avg)

	res,err = client.EsCon.Search(Params["index"]).
		Type(Params["type"]).
		Size(0).
		Aggregation(Params["aggregation_name"], aggs).
		//Sort(Params["sort"],sort_type).
		Timeout(Timeout).
		Do(context.Background())

	if err != nil {
		println("func Aggregation error:"+err.Error())
	}
	println("func Aggregation here 297")

	return res

}

或许猛一看这么大段代码有些晕,没关系,代码片段中已写注释。下面单独介绍一下:

1、连接初始化


func  init(){
	elastic.SetSniff(false) //必须 关闭 Sniffing
	//es 配置
	var err error
	//EsClient.EsCon, err = elastic.NewClient(elastic.SetURL(host))
	EsClient.EsCon, err =elastic.NewClient(
		elastic.SetURL(host),
		elastic.SetSniff(false),
		elastic.SetHealthcheckInterval(10*time.Second),
		elastic.SetGzip(true),
		elastic.SetErrorLog(log.New(os.Stderr, "ELASTIC ", log.LstdFlags)),
		elastic.SetInfoLog(log.New(os.Stdout, "", log.LstdFlags)),
	)
	if err != nil {
		panic(err)
	}
	info, code, err := EsClient.EsCon.Ping(host).Do(context.Background())
	if err != nil {
		panic(err)
	}

	fmt.Printf("Elasticsearch returned with code %d and version %s\n", code, info.Version.Number)

	esversion, err := EsClient.EsCon.ElasticsearchVersion(host)
	if err != nil {
		panic(err)
	}
	fmt.Printf("Elasticsearch version %s\n", esversion)
	fmt.Println("conn es succ",EsClient.EsCon)
}

 

注意:其中第一行,当 sniffing 模式被启用(默认启用),Elastic 使用 Nodes Info API 查找群集中的所有节点。 找到这些节点后,它会定期更新内部连接列表。现在发现的大多数连接问题,都是因为 Elastic 无法调用节点信息API 或无法访问到 Nodes Info API 提供的 IP:Port组合地址 。

elastic.SetSniff(false) //必须 关闭 Sniffing

假设不关闭,报错信息

no Elasticsearch node available

把连接成功的实例存入全局变量,供其他函数调用,简易连接es方法如下:

EsClient.EsCon, err = elastic.NewClient(elastic.SetURL(host))

 

2、封装可调用函数并使用

在连接es后,根据sdk包里的函数,来封装我们需要的函数供业务使用。目前只使用了后面两个函数:

List: 可搜索的分页列表函数

Aggregation:聚合平均使用的函数(可扩展其他聚合方法,比如 最大值,最小值)

 

1⃣️List 函数,其中使用Params 里的key值都是自己定义的,比如约定排序是sort ,排序方式是sort_type,搜索是queryString等。如下:

func (client *EsClientType) List(Params map[string]string) *elastic.SearchResult {
	var res *elastic.SearchResult
	var err error
	size, _ := strconv.Atoi(Params["size"])
	page, _ := strconv.Atoi(Params["page"])
	q := elastic.NewQueryStringQuery(Params["queryString"])

	//排序类型 desc asc es 中只使用 bool 值  true or false
	sort_type :=true
	if Params["sort_type"]=="desc" {
		sort_type =false
	}
	//fmt.Printf(" sort info  %s,%s\n", Params["sort"],Params["sort_type"])
	if size < 0 || page < 0 {
		fmt.Printf("param error")
		return res
	}
	if len(Params["queryString"]) >0 {
		res,err = client.EsCon.Search(Params["index"]).
			Type(Params["type"]).
			Query(q).
			Size(size).
			From((page)*size).
			Sort(Params["sort"],sort_type).
			Timeout(Timeout).
			Do(context.Background())

	}else{
		res,err = client.EsCon.Search(Params["index"]).
			Type(Params["type"]).
			Size(size).
			From((page)*size).
			Sort(Params["sort"],sort_type).
			//SortBy(elastic.NewFieldSort("add_time").UnmappedType("long").Desc(), elastic.NewScoreSort()).
			Timeout(Timeout).
			Do(context.Background())
	}

	if err != nil {
		println("func list error:"+err.Error())
	}
	return res

}

调用:

//连接es
res:=connections.EsClient.List(params)

解析返回值:

//数据总数
TotalHits:=res.Hits.TotalHits

把内存地址里的值解析出来,主要使用函数 

json.Unmarshal()

下面代码中有一个if 判断 type,主要是区分 t 的结构体。

type Employee1 struct {
	Id 			string    `json:"_id"`
	IP 			string    `json:"ip"`
	Visit_count int       `json:"visit_count"`
	Add_time 	string    `json:"add_time"`

}


type Employee2 struct {
	Id 			string     `json:"_id"`
	Query_time 	float64    `json:"query_time"`
	Host_ip 	string     `json:"host_ip"`
	Db 			string     `json:"db"`
	Timestamp   time.Time  `json:"@timestamp"`
	Sql         string     `json:"sql"`

}
list :=make([]interface{},0)

	if TotalHits > 0 {
		fmt.Printf("Found a total of %d hits.\n", TotalHits)
		// Iterate through results
		for _, hit := range res.Hits.Hits {
			// Deserialize hit.Source into a Employee (could also be just a map[string]interface{}).

			if types ==1 {
				var t Employee1
				err := json.Unmarshal(*hit.Source, &t)
				if err != nil {
					// Deserialization failed
				}
				t.Id = hit.Id
				// Work with Employee
				list = append(list, t)
			}else if types==2 {
				var t Employee2
				err := json.Unmarshal(*hit.Source, &t)
				if err != nil {
					// Deserialization failed
				}
				t.Id = hit.Id

				//t.Timestamp=2019-10-24T23:59:59.000000000+08:00"
				// Work with Employee
				list = append(list, t)

			}
		}
	} else {
		// No hits
		fmt.Print("Found no result\n")
	}

 

2⃣️Aggregation函数,根据自己需要聚合的参数来约定,比如 需要聚合的求平均值:

//聚合 平均 可用
func (client *EsClientType) Aggregation(Params map[string]string) *elastic.SearchResult {
	var res *elastic.SearchResult
	var err error

	//需要聚合的指标 求平均
	avg := elastic.NewAvgAggregation().Field(Params["avg"])
	//单位时间和指定字段
	aggs := elastic.NewDateHistogramAggregation().
		Interval("day").
		Field(Params["field"]).
		//TimeZone("Asia/Shanghai").
		SubAggregation(Params["agg_name"], avg)

	res,err = client.EsCon.Search(Params["index"]).
		Type(Params["type"]).
		Size(0).
		Aggregation(Params["aggregation_name"], aggs).
		//Sort(Params["sort"],sort_type).
		Timeout(Timeout).
		Do(context.Background())

	if err != nil {
		println("func Aggregation error:"+err.Error())
	}
	println("func Aggregation here 297")

	return res

}

调用:

//连接es
res:=connections.EsClient.Aggregation(params)

我使用的参数对应关系如下:

	params["avg"] ="query_time" //需要聚合的字段
	params["field"]="@timestamp" //聚合的单位时间字段
	params["agg_name"]="avg_time" //聚合后的字段名
	params["aggregation_name"]="AVG_Metric" //聚合的名字
type Aggregations struct {
	AVG_Metric AVG_Metric `json:"AVG_Metric"`
}

type AVG_Metric struct {
	Buckets []Metric `json:"buckets"`
}

type Metric struct {
	Avg_time   Value `json:"avg_time"`
}

type Value struct {
	Value float64 `json:"value"`
}

接下来就是解析聚合后的返回值 :

    //数据总数
	TotalHits:=res.Hits.TotalHits
	var avg_time Value

	if TotalHits > 0 {
		fmt.Printf("Found a total of %d hits\n", TotalHits)
		fmt.Println("开始")
		var m *[]Metric
		term, _ := res.Aggregations.Terms("AVG_Metric")

		for _, bucket := range term.Aggregations {
			b, _ := bucket.MarshalJSON()
			//fmt.Println(string(b))
			aa := json.Unmarshal(b, &m)
			fmt.Println(aa)
			for _, v := range *m {
				avg_time = v.Avg_time
			}
		}
		fmt.Println("结束")

	} else {
		// No hits
		fmt.Print("Found no result\n")
	}

 

本项目的具体操作和具体使用将在后续章节中详细说明。

希望本文对你学习有所帮助,感谢您的阅读。

 

你可能感兴趣的:(go,gin,elasticsearch,go,gin,elasticsearch)