123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505 |
- package dao
- import (
- "bytes"
- "context"
- "encoding/json"
- "fmt"
- "io"
- "strings"
- "go-common/app/admin/main/search/model"
- "go-common/library/log"
- "gopkg.in/olivere/elastic.v5"
- )
- // UpdateMapBulk (Deprecated).
- func (d *Dao) UpdateMapBulk(c context.Context, esName string, bulkData []BulkMapItem) (err error) {
- bulkRequest := d.esPool[esName].Bulk()
- for _, b := range bulkData {
- request := elastic.NewBulkUpdateRequest().Index(b.IndexName()).Type(b.IndexType()).Id(b.IndexID()).Doc(b.PField()).DocAsUpsert(true)
- bulkRequest.Add(request)
- }
- if _, err = bulkRequest.Do(c); err != nil {
- log.Error("esName(%s) bulk error(%v)", esName, err)
- }
- return
- }
- // UpdateBulk (Deprecated).
- func (d *Dao) UpdateBulk(c context.Context, esName string, bulkData []BulkItem) (err error) {
- bulkRequest := d.esPool[esName].Bulk()
- for _, b := range bulkData {
- request := elastic.NewBulkUpdateRequest().Index(b.IndexName()).Type(b.IndexType()).Id(b.IndexID()).Doc(b).DocAsUpsert(true)
- bulkRequest.Add(request)
- }
- if _, err = bulkRequest.Do(c); err != nil {
- log.Error("esName(%s) bulk error(%v)", esName, err)
- }
- return
- }
- // UpsertBulk 为了替换UpdateMapBulk和UpdateBulk .
- func (d *Dao) UpsertBulk(c context.Context, esCluster string, up *model.UpsertParams) (err error) {
- es, ok := d.esPool[esCluster]
- if !ok {
- log.Error("esCluster(%s) not exists", esCluster)
- return
- }
- bulkRequest := es.Bulk()
- for _, b := range up.UpsertBody {
- request := elastic.NewBulkUpdateRequest().Index(b.IndexName).Type(b.IndexType).Id(b.IndexID).Doc(b.Doc)
- if up.Insert {
- request.DocAsUpsert(true)
- }
- //fmt.Println(request)
- bulkRequest.Add(request)
- }
- if _, err = bulkRequest.Do(c); err != nil {
- log.Error("esCluster(%s) bulk error(%v)", esCluster, err)
- }
- return
- }
- // searchResult get result from ES. (Deprecated) v3迁移完要删掉.
- func (d *Dao) searchResult(c context.Context, esClusterName, indexName string, query elastic.Query, bsp *model.BasicSearchParams) (res *model.SearchResult, err error) {
- res = &model.SearchResult{Debug: ""}
- if bsp.Debug {
- if src, e := query.Source(); e == nil {
- if data, er := json.Marshal(src); er == nil {
- res = &model.SearchResult{Debug: string(data)}
- } else {
- err = er
- log.Error("searchResult query.Source.json.Marshal error(%v)", err)
- return
- }
- } else {
- err = e
- log.Error("searchResult query.Source error(%v)", err)
- return
- }
- }
- if _, ok := d.esPool[esClusterName]; !ok {
- PromError(fmt.Sprintf("es:集群不存在%s", esClusterName), "s.dao.searchResult indexName:%s", indexName)
- res = &model.SearchResult{Debug: fmt.Sprintf("es:集群不存在%s, %s", esClusterName, res.Debug)}
- return
- }
- // multi sort
- sorterSlice := []elastic.Sorter{}
- if bsp.KW != "" && bsp.ScoreFirst {
- sorterSlice = append(sorterSlice, elastic.NewScoreSort().Desc())
- }
- for i, d := range bsp.Order {
- if len(bsp.Sort) < i+1 {
- if bsp.Sort[0] == "desc" {
- sorterSlice = append(sorterSlice, elastic.NewFieldSort(d).Desc())
- } else {
- sorterSlice = append(sorterSlice, elastic.NewFieldSort(d).Asc())
- }
- } else {
- if bsp.Sort[i] == "desc" {
- sorterSlice = append(sorterSlice, elastic.NewFieldSort(d).Desc())
- } else {
- sorterSlice = append(sorterSlice, elastic.NewFieldSort(d).Asc())
- }
- }
- }
- if bsp.KW != "" && !bsp.ScoreFirst {
- sorterSlice = append(sorterSlice, elastic.NewScoreSort().Desc())
- }
- // source
- fsc := elastic.NewFetchSourceContext(true).Include(bsp.Source...)
- // highlight
- hl := elastic.NewHighlight()
- if bsp.Highlight && len(bsp.KwFields) > 0 {
- for _, v := range bsp.KwFields {
- hl = hl.Fields(elastic.NewHighlighterField(v))
- }
- hl = hl.PreTags("<em class=\"keyword\">").PostTags("</em>")
- }
- // from + size = 10,000
- from := (bsp.Pn - 1) * bsp.Ps
- size := bsp.Ps
- if (from + size) > 10000 {
- from = 10000 - size
- }
- // do
- searchResult, err := d.esPool[esClusterName].
- Search().Index(indexName).
- Highlight(hl).
- Query(query).
- SortBy(sorterSlice...).
- From(from).
- Size(size).
- Pretty(true).
- FetchSourceContext(fsc).
- Do(context.Background())
- if err != nil {
- PromError(fmt.Sprintf("es:执行查询失败%s ", esClusterName), "%v", err)
- res = &model.SearchResult{Debug: res.Debug + "es:执行查询失败"}
- return
- }
- var data []json.RawMessage
- b := bytes.Buffer{}
- b.WriteString("{")
- b.WriteString("}")
- for _, hit := range searchResult.Hits.Hits {
- var t json.RawMessage
- e := json.Unmarshal(*hit.Source, &t)
- if e != nil {
- PromError(fmt.Sprintf("es:%s 索引有脏数据", esClusterName), "s.dao.SearchArchiveCheck(%d,%d) error(%v) ", bsp.Pn*bsp.Ps, bsp.Ps, e)
- continue
- }
- data = append(data, t)
- // highlight
- if len(hit.Highlight) > 0 {
- b, _ := json.Marshal(hit.Highlight)
- h := []byte(string(b))
- data = append(data, h)
- } else if bsp.Highlight {
- data = append(data, b.Bytes()) //保证在高亮情况下,肯定有一对数据
- }
- }
- if len(data) == 0 {
- data = []json.RawMessage{}
- }
- res = &model.SearchResult{
- Order: strings.Join(bsp.Order, ","),
- Sort: strings.Join(bsp.Sort, ","),
- Result: data,
- Debug: res.Debug,
- Page: &model.Page{
- Pn: bsp.Pn,
- Ps: bsp.Ps,
- Total: searchResult.Hits.TotalHits,
- },
- }
- return
- }
- // QueryResult query result from ES.
- func (d *Dao) QueryResult(c context.Context, query elastic.Query, sp *model.QueryParams, qbDebug *model.QueryDebugResult) (res *model.QueryResult, qrDebug *model.QueryDebugResult, err error) {
- qrDebug = &model.QueryDebugResult{}
- if qbDebug != nil {
- qrDebug = qbDebug
- }
- esCluster := sp.AppIDConf.ESCluster
- if _, ok := d.esPool[esCluster]; !ok {
- qrDebug.AddErrMsg("es:集群不存在" + esCluster)
- return
- }
- if sp.DebugLevel != 0 {
- qrDebug.Mapping, err = d.esPool[esCluster].GetMapping().Index(sp.QueryBody.From).Do(context.Background())
- }
- // 低级别debug,在dsl执行前退出
- if sp.DebugLevel == 1 {
- return
- }
- // multi sort
- sorterSlice := []elastic.Sorter{}
- if len(sp.QueryBody.Where.Like) > 0 && sp.QueryBody.OrderScoreFirst { // like 长度 > 0,但里面是空的也是个问题
- sorterSlice = append(sorterSlice, elastic.NewScoreSort().Desc())
- }
- for _, i := range sp.QueryBody.Order {
- for k, v := range i {
- if v == "asc" {
- sorterSlice = append(sorterSlice, elastic.NewFieldSort(k).Asc())
- } else {
- sorterSlice = append(sorterSlice, elastic.NewFieldSort(k).Desc())
- }
- }
- }
- if len(sp.QueryBody.Where.Like) > 0 && sp.QueryBody.OrderScoreFirst {
- sorterSlice = append(sorterSlice, elastic.NewScoreSort().Desc())
- }
- // source
- fsc := elastic.NewFetchSourceContext(true).Include(sp.QueryBody.Fields...)
- // highlight
- hl := elastic.NewHighlight()
- if sp.QueryBody.Highlight && len(sp.QueryBody.Where.Like) > 0 {
- for _, v := range sp.QueryBody.Where.Like {
- for _, field := range v.KWFields {
- hl = hl.Fields(elastic.NewHighlighterField(field))
- }
- }
- hl = hl.PreTags("<em class=\"keyword\">").PostTags("</em>")
- }
- // from + size = 10,000
- maxRows := 10000
- if b, ok := model.PermConf["oht"][sp.Business]; ok && b == "true" {
- maxRows = 100000
- }
- from := (sp.QueryBody.Pn - 1) * sp.QueryBody.Ps
- size := sp.QueryBody.Ps
- if (from + size) > maxRows {
- from = maxRows - size
- }
- // Scroll
- if sp.QueryBody.Scroll == true {
- var (
- tList []json.RawMessage
- tLen int
- ScrollID = ""
- )
- res = &model.QueryResult{}
- esCluster := sp.AppIDConf.ESCluster
- eSearch, ok := d.esPool[esCluster]
- if !ok {
- PromError(fmt.Sprintf("es:集群不存在%s", esCluster), "s.dao.searchResult indexName:%s", esCluster)
- return
- }
- fsc := elastic.NewFetchSourceContext(true).Include(sp.QueryBody.Fields...)
- // multi sort
- sorterSlice := []elastic.Sorter{}
- if len(sp.QueryBody.Where.Like) > 0 && sp.QueryBody.OrderScoreFirst { // like 长度 > 0,但里面是空的也是个问题
- sorterSlice = append(sorterSlice, elastic.NewScoreSort().Desc())
- }
- for _, i := range sp.QueryBody.Order {
- for k, v := range i {
- if v == "asc" {
- sorterSlice = append(sorterSlice, elastic.NewFieldSort(k).Asc())
- } else {
- sorterSlice = append(sorterSlice, elastic.NewFieldSort(k).Desc())
- }
- }
- }
- if len(sp.QueryBody.Where.Like) > 0 && !sp.QueryBody.OrderScoreFirst {
- sorterSlice = append(sorterSlice, elastic.NewScoreSort().Desc())
- }
- for {
- searchResult, err := eSearch.Scroll().Index(sp.QueryBody.From).
- Query(query).FetchSourceContext(fsc).Size(sp.QueryBody.Ps).Scroll("1m").ScrollId(ScrollID).SortBy(sorterSlice...).Do(c)
- if err == io.EOF {
- break
- } else if err != nil {
- PromError(fmt.Sprintf("es:执行查询失败%s ", "Scroll"), "es:执行查询失败%v", err)
- break
- }
- ScrollID = searchResult.ScrollId
- for _, hit := range searchResult.Hits.Hits {
- var t json.RawMessage
- if err = json.Unmarshal(*hit.Source, &t); err != nil {
- PromError(fmt.Sprintf("es:Unmarshal%s ", "Scroll"), "es:Unmarshal%v", err)
- break
- }
- tList = append(tList, t)
- tLen++
- if tLen >= sp.QueryBody.Pn*sp.QueryBody.Ps {
- goto ClearScroll
- }
- }
- }
- ClearScroll:
- go eSearch.ClearScroll().ScrollId(ScrollID).Do(context.Background())
- if res.Result, err = json.Marshal(tList); err != nil {
- PromError(fmt.Sprintf("es:Unmarshal%s ", "Scroll"), "es:Unmarshal%v", err)
- return
- }
- return
- }
- // do
- searchPrepare := d.esPool[esCluster].
- Search().Index(sp.QueryBody.From).
- Highlight(hl).
- Query(query).
- SortBy(sorterSlice...).
- From(from).
- Size(size).
- FetchSourceContext(fsc).IgnoreUnavailable(true).AllowNoIndices(true)
- if ec, ok := model.PermConf["es_cache"][sp.Business]; ok && ec == "true" {
- searchPrepare.RequestCache(true)
- }
- if rt, ok := model.PermConf["routing"][sp.Business]; ok {
- routing := make([]string, 0, 1)
- if sp.QueryBody.Where.EQ != nil {
- if eq, ok := sp.QueryBody.Where.EQ[rt]; ok {
- routing = append(routing, fmt.Sprintf("%v", eq))
- }
- }
- if sp.QueryBody.Where.In != nil {
- if in, ok := sp.QueryBody.Where.In[rt]; ok {
- for _, v := range in {
- routing = append(routing, fmt.Sprintf("%v", v))
- }
- }
- }
- if len(routing) == 0 {
- qrDebug.AddErrMsg("es:路由不存在" + rt)
- return
- }
- searchPrepare.Routing(routing...)
- }
- if sp.DebugLevel == 2 {
- searchPrepare.Profile(true)
- }
- // Enhanced
- for _, v := range sp.QueryBody.Where.Enhanced {
- aggKey := v.Mode + "_" + v.Field
- switch v.Mode {
- case model.EnhancedModeGroupBy:
- aggs := elastic.NewTermsAggregation()
- aggs = aggs.Field(v.Field).Size(1000) //要和业务方确定具体值
- searchPrepare.Aggregation(aggKey, aggs)
- case model.EnhancedModeCollapse, model.EnhancedModeDistinct:
- collapse := elastic.NewCollapseBuilder(v.Field).MaxConcurrentGroupRequests(1)
- innerHit := elastic.NewInnerHit().Name("last_one").Size(1)
- for _, v := range v.Order {
- for field, sort := range v {
- if sort == "desc" {
- innerHit.Sort(field, false)
- } else {
- innerHit.Sort(field, true)
- }
- }
- }
- if len(v.Order) > 0 {
- collapse.InnerHit(innerHit)
- }
- searchPrepare.Collapse(collapse)
- case model.EnhancedModeSum:
- aggs := elastic.NewSumAggregation()
- aggs = aggs.Field(v.Field)
- searchPrepare.Aggregation(aggKey, aggs)
- case model.EnhancedModeDistinctCount:
- aggs := elastic.NewCardinalityAggregation()
- aggs = aggs.Field(v.Field)
- searchPrepare.Aggregation(aggKey, aggs)
- }
- }
- searchResult, err := searchPrepare.Do(context.Background())
- if err != nil {
- qrDebug.AddErrMsg(fmt.Sprintf("es:执行查询失败%s. %v", esCluster, err))
- PromError(fmt.Sprintf("es:执行查询失败%s ", esCluster), "%v", err)
- return
- }
- // data
- data := json.RawMessage{}
- docHits := []json.RawMessage{}
- docBuckets := map[string][]map[string]*json.RawMessage{}
- b := bytes.Buffer{}
- b.WriteString("{")
- b.WriteString("}")
- for _, hit := range searchResult.Hits.Hits {
- var t json.RawMessage
- e := json.Unmarshal(*hit.Source, &t)
- if e != nil {
- PromError(fmt.Sprintf("es:%s 索引有脏数据", esCluster), "s.dao.SearchArchiveCheck(%d,%d) error(%v) ", sp.QueryBody.Pn*sp.QueryBody.Ps, sp.QueryBody.Ps, e)
- continue
- }
- docHits = append(docHits, t)
- // highlight
- if len(hit.Highlight) > 0 {
- b, _ := json.Marshal(hit.Highlight)
- docHits = append(docHits, b)
- } else if sp.QueryBody.Highlight {
- docHits = append(docHits, b.Bytes()) //保证在高亮情况下,肯定有一对数据
- }
- }
- if len(docHits) > 0 {
- if doc, er := json.Marshal(docHits); er != nil {
- qrDebug.AddErrMsg(fmt.Sprintf("es:Unmarshal docHits es:Unmarshal%v ", er))
- PromError(fmt.Sprintf("es:Unmarshal%s ", "docHits"), "es:Unmarshal%v", er)
- } else {
- data = doc
- }
- } else {
- h := bytes.Buffer{}
- h.WriteString("[")
- h.WriteString("]")
- data = h.Bytes()
- }
- // data overwrite
- for _, v := range sp.QueryBody.Where.Enhanced {
- key := v.Mode + "_" + v.Field
- switch v.Mode {
- case model.EnhancedModeGroupBy:
- result, ok := searchResult.Aggregations.Terms(key)
- if !ok {
- PromError(fmt.Sprintf("es:Unmarshal%s ", key), "es:Unmarshal%v", err)
- continue
- }
- for _, b := range result.Buckets {
- docBuckets[key] = append(docBuckets[key], b.Aggregations)
- }
- data = b.Bytes() //保证无数据情况下,有正常返回
- case model.EnhancedModeSum:
- result, ok := searchResult.Aggregations.Sum(key)
- if !ok {
- PromError(fmt.Sprintf("es:Unmarshal%s ", key), "es:Unmarshal%v", err)
- continue
- }
- docBuckets[key] = append(docBuckets[key], result.Aggregations)
- data = b.Bytes() //保证无数据情况下,有正常返回
- case model.EnhancedModeDistinctCount:
- result, ok := searchResult.Aggregations.Cardinality(key)
- if !ok {
- PromError(fmt.Sprintf("es:Unmarshal%s ", key), "es:Unmarshal%v", err)
- continue
- }
- docBuckets[key] = append(docBuckets[key], result.Aggregations)
- data = b.Bytes() //保证无数据情况下,有正常返回
- default:
- // other modes...
- }
- }
- if len(docBuckets) > 0 {
- if doc, er := json.Marshal(docBuckets); er != nil {
- qrDebug.AddErrMsg(fmt.Sprintf("es:Unmarshal docBuckets es:Unmarshal%v", er))
- PromError(fmt.Sprintf("es:Unmarshal%s ", "docBuckets"), "es:Unmarshal%v", er)
- } else {
- data = doc
- }
- }
- order := []string{}
- sort := []string{}
- for _, i := range sp.QueryBody.Order {
- for k, v := range i {
- order = append(order, k)
- sort = append(sort, v)
- }
- }
- res = &model.QueryResult{
- Order: strings.Join(order, ","),
- Sort: strings.Join(sort, ","),
- Result: data,
- Page: &model.Page{
- Pn: sp.QueryBody.Pn,
- Ps: sp.QueryBody.Ps,
- Total: searchResult.Hits.TotalHits,
- },
- }
- //(默认的debug)高级别debug,在dsl执行后退出
- if sp.DebugLevel == 2 {
- qrDebug.Profile = searchResult.Profile
- return
- }
- return
- }
- // BulkIndex .
- func (d *Dao) BulkIndex(c context.Context, esName string, bulkData []BulkItem) (err error) {
- bulkRequest := d.esPool[esName].Bulk()
- for _, b := range bulkData {
- request := elastic.NewBulkIndexRequest().Index(b.IndexName()).Type(b.IndexType()).Id(b.IndexID()).Doc(b)
- bulkRequest.Add(request)
- }
- if _, err = bulkRequest.Do(c); err != nil {
- log.Error("esName(%s) bulk error(%v)", esName, err)
- }
- return
- }
- // ExistIndex .
- func (d *Dao) ExistIndex(c context.Context, esClusterName, indexName string) (exist bool, err error) {
- if _, ok := d.esPool[esClusterName]; !ok {
- PromError(fmt.Sprintf("es:集群不存在%s", esClusterName), "s.dao.searchResult indexName:%s", indexName)
- err = fmt.Errorf("集群不存在")
- return
- }
- exist, err = d.esPool[esClusterName].IndexExists(indexName).Do(c)
- return
- }
|