业务需求:ES中修改历史数据中的某个字段或增加字段,历史上亿的数据,使用游标分页查询的方式查询大量历史数据。
《elasticsearch权威指南》直接看官网在线版的
使用第三方包:github.com/olivere/elastic
esclient.go
package ESOperation
import (
"BehaviorAnalysisSystem/ESDataModify/global"
"context"
"encoding/json"
"github.com/olivere/elastic"
"github.com/olivere/elastic/config"
"strings"
"time"
)
//ES中时间字段名,UTC时间
const (
TimeKey = "MARK_TIME"
TypeKey = "docs" //ES中文档类型名
)
type ESClient struct {
elsticClent *elastic.Client
}
func NewESClient() *ESClient {
return &ESClient{}
}
// 初始化客户端
func (this *ESClient) ESInit(ip, port string) error {
var err error
//获取ES信息
URL := "http://" + ip + ":" + port + "/"
global.Log.Debug("ES信息:%v", URL)
//获取elastic日志路径
Infolog, _, _ := global.GetElasticlogPath()
global.Log.Debug("elastic日志路径:%v", Infolog)
//elastic配置
elsticConfig := config.Config{
URL: URL,
Infolog: Infolog,
Errorlog: Infolog,
Tracelog: Infolog,
}
this.elsticClent, err = elastic.NewClientFromConfig(&elsticConfig)
if err != nil {
global.Log.Error("ES客户端初始化失败,error info:%v", err)
return err
}
global.Log.Info("ES客户端初始化成功")
//测试ES
info, code, err := this.elsticClent.Ping(URL).Do(context.Background())
if err != nil {
global.Log.Error("[Init]elsticClent.Ping error,error info:%v", err.Error())
return err
}
global.Log.Debug("elasticsearch returned with code %d and version %s:", code, info.Version.Number)
return nil
}
// 查询
func (this *ESClient) ESQuery(indexKey, typeKey, startTime, endTime string) error {
//转换时间格式
//startUTCTime := this.trans2UTCTime(startTime)
//endUTCTime := this.trans2UTCTime(endTime)
startUTCTime := startTime
endUTCTime := endTime
//查询时间段
q := elastic.NewRangeQuery(TimeKey)
q.Gte(startUTCTime)
q.Lt(endUTCTime)
global.Log.Debug("timeKey:%s,startTime:%s,endTime:%s", TimeKey, startUTCTime, endUTCTime)
//查询ES
var startIndex int = 0
//ES一次查询数量
const OnceQueryNum int = 1
searchResult, err := this.elsticClent.Search().
Index(indexKey).
Type(typeKey).
Query(q).
From(startIndex).Size(OnceQueryNum).
Do(context.TODO())
if nil != err {
global.Log.Error("ES查询错误,error info:%v", err.Error())
return err
}
//判断是否查询到文档
if searchResult.Hits == nil {
global.Log.Error("ES查询到的文档为nil")
return err
}
global.Log.Debug("ES查询到的命中数据条数TotalHits:%v", searchResult.Hits.TotalHits)
//遍历查询到的文档组合服务操作数据对象切片
for _, hit := range searchResult.Hits.Hits {
item := make(map[string]interface{})
err := json.Unmarshal(*hit.Source, &item)
if err != nil {
global.Log.Error("[GetServiceOperation]json.Unmarshal error,error info:%v", err.Error())
continue
}
global.Log.Debug("ES查询到的数据条数TotalHits:%v", hit)
global.Log.Debug("item:%v", item)
global.Log.Debug("SS_ID:%v", item["SS_ID"].(string))
if _, ok := item["SS_APE_INST_ADDR_CODE"]; ok {
areaCode := "610502" //存在
item["SS_APE_INST_ADDR_CODE"] = areaCode
}
if _, ok := item["SS_APE_INST_ADDR"]; ok {
//存在
areaCode := "610500"
item["SS_APE_INST_ADDR"] = areaCode
global.Log.Debug("存在:%v", item["SS_APE_INST_ADDR"].(string))
} else {
areaCode := "610500"
item["SS_APE_INST_ADDR"] = areaCode
global.Log.Debug("不存在")
}
//修改原始数据
err = this.ESModify(hit.Index, hit.Type, hit.Id, item)
if err != nil {
return err
}
}
//判断是否查询完
if int64(startIndex+len(searchResult.Hits.Hits)) >= searchResult.Hits.TotalHits {
//break
}
//更新下次查询起始位置
startIndex += len(searchResult.Hits.Hits)
return nil
}
// 游标查询
func (this *ESClient) ESScrollQuery(indexKey, keepAlive string, size int) error {
//初始化ScrollID 取出第一条数据
res, err := this.elsticClent.Scroll(indexKey).Scroll(keepAlive).Size(size).Do(context.TODO())
if err != nil {
global.Log.Error("elastic首次查询游标失败:%v", err)
return err
}
global.Log.Debug("首次游标分页查询ScrollID:%v", res.ScrollId)
if res.ScrollId == "" {
global.Log.Error("elastic首次查询游标为空:%v", indexKey)
return err
}
//遍历查询到的文档组合服务操作数据对象切片
for _, hit := range res.Hits.Hits {
item := make(map[string]interface{})
err := json.Unmarshal(*hit.Source, &item)
if err != nil {
global.Log.Error("json.Unmarshal error,error info:%v", err.Error())
continue
}
//根据设备ID查找组织编码
if _, ok := item["DEVICE_ID"]; !ok {
global.Log.Debug("设备ID不存在")
continue
}
global.Log.Debug("设备ID存在:%v", item["DEVICE_ID"])
if _, ok := item["SS_APE_INST_ADDR"]; ok {
areaCode := "610500" //存在
item["SS_APE_INST_ADDR"] = areaCode
} else {
areaCode := "610500"
item["SS_APE_INST_ADDR"] = areaCode
}
//修改原始数据
err = this.ESModify(hit.Index, hit.Type, hit.Id, item)
if err != nil {
return err
}
global.Log.Debug("ID:%v, *hit.Source:%v", hit.Id, string(*hit.Source)) //string(*hit.Source)
}
//记录分页查询起始位置
startIndex := len(res.Hits.Hits)
global.Log.Debug("索引%v数据总量:%v,已经获取:%v", indexKey, res.Hits.TotalHits, startIndex)
scrollID := res.ScrollId
for {
// 根据ScrollID检索下一个批次的结果,注意:初始搜索请求和每个后续滚动请求返回一个新的_scroll_id,只有最近的_scroll_id才能被使用。
searchResult, err := this.elsticClent.Scroll("1m").ScrollId(scrollID).Do(context.TODO())
if err != nil && !strings.Contains(err.Error(), "EOF") {
global.Log.Error("elastic游标查询数据失败:%v", err)
return err
}
//判断游标ID
if searchResult.ScrollId == "" {
global.Log.Error("elastic首次查询游标为空:%v", indexKey)
return err
}
scrollID = res.ScrollId
//判断是否查询到文档
if searchResult.Hits == nil {
global.Log.Error("游标查询到的文档为nil")
return err
}
global.Log.Debug("ES查询到的命中数据条数TotalHits:%v", searchResult.Hits.TotalHits)
//遍历查询到的文档组合服务操作数据对象切片
for _, hit := range searchResult.Hits.Hits {
item := make(map[string]interface{})
err := json.Unmarshal(*hit.Source, &item)
if err != nil {
global.Log.Error("json.Unmarshal error,error info:%v", err.Error())
continue
}
global.Log.Debug("SS_ID:%v", item["SS_ID"].(string))
if _, ok := item["SS_APE_INST_ADDR"]; ok {
areaCode := "610500" //存在
item["SS_APE_INST_ADDR"] = areaCode
} else {
areaCode := "610500"
item["SS_APE_INST_ADDR"] = areaCode
}
//修改原始数据
err = this.ESModify(hit.Index, hit.Type, hit.Id, item)
if err != nil {
continue
}
global.Log.Debug("数据ID:%v, *hit.Source:%v", hit.Id, string(*hit.Source)) //string(*hit.Source)
}
//判断是否分页查询完毕
if int64(startIndex+len(searchResult.Hits.Hits)) >= searchResult.Hits.TotalHits {
break
}
//更新下次分页查询起始位置
startIndex += len(searchResult.Hits.Hits)
global.Log.Debug("索引%v数据总量:%v,已经获取:%v", indexKey, searchResult.Hits.TotalHits, startIndex)
}
// 清除游标
_, err = this.elsticClent.ClearScroll().ScrollId(res.ScrollId).Do(context.TODO())
if err != nil {
global.Log.Error("清除游标失败,error info:%v", err)
return err
}
global.Log.Debug("清除游标成功")
return nil
}
// 修改原始数据
func (this *ESClient) ESModify(index, typekey, id string, item map[string]interface{}) error {
global.Log.Debug("index:%v,type:%v,id:%v", index, typekey, id)
_, err := this.elsticClent.Update().
Index(index).
Type(typekey).
Id(id).
Doc(item).
Do(context.Background())
if err != nil {
global.Log.Error("ES修改失败,index:%v,type:%v,id:%v,error info:%v", index, typekey, id, err)
}
global.Log.Error("修改索引%v成功", index)
return nil
}
// 增加字段原始数据
func (this *ESClient) ESAddFiled(index, typekey, id string, item map[string]interface{}) error {
global.Log.Debug("index:%v,type:%v,id:%v", index, typekey, id)
_, err := this.elsticClent.Update().
Index(index).
Type(typekey).
Id(id).
Doc(item).
Do(context.Background())
if err != nil {
global.Log.Error("ES增加字段失败,index:%v,type:%v,id:%v,error info:%v", index, typekey, id, err)
}
global.Log.Error("增加索引%v成功", index)
return nil
}
func (this *ESClient) trans2UTCTime(strLocalTime string) string {
localTime, _ := time.ParseInLocation("2006-01-02 15:04:05", strLocalTime, time.Local)
utcTime := localTime.In(time.UTC)
strUtcTime := utcTime.Format("2006-01-02T15:04:05.000Z")
return strUtcTime
}
测试用例:
package ESOperation
import (
"BehaviorAnalysisSystem/ESDataModify/global"
"testing"
)
const (
IndexKey = "t_vias_sna_motor_vehicle*"
)
func TestESClient_ESInit(t *testing.T) {
if !global.Init("test") {
return
}
defer func() {
global.Log.Flush()
}()
es := NewESClient()
err := es.ESInit("172.20.32.244", "9200")
if err != nil {
global.Log.Error("初始化失败")
}
global.Log.Info("初始化成功")
//err = es.ESQuery(IndexKey, TypeKey, "2010-08-01 13:12:59", "2010-08-01 13:13:00")
//if err != nil {
// global.Log.Error("查询失败")
//}
//global.Log.Error("查询成功")
err = es.ESScrollQuery(IndexKey, "5m", 1000)
if err != nil {
global.Log.Error("查询失败")
}
global.Log.Error("查询完毕")
}
运行效果:
head插件查询:
欢迎不吝指出问题,加以改正!