Golang學習篇—elasticsearch遊標分頁查詢

1. 需求

業務需求:ES中修改歷史數據中的某個字段或增加字段,歷史上億的數據,使用遊標分頁查詢的方式查詢大量歷史數據。

《elasticsearch權威指南》直接看官網在線版的

2. 使用第三方包

使用第三方包:github.com/olivere/elastic

3. 代碼樣例 

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插件查詢:


歡迎不吝指出問題,加以改正!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章