github.com/siglens/siglens@v0.0.0-20240328180423-f7ce9ae441ed/pkg/es/reader/esQueryHandler.go (about) 1 /* 2 Copyright 2023. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package reader 18 19 import ( 20 "net/url" 21 "strconv" 22 "strings" 23 "time" 24 25 "github.com/siglens/siglens/pkg/es/query" 26 rutils "github.com/siglens/siglens/pkg/readerUtils" 27 "github.com/siglens/siglens/pkg/scroll" 28 "github.com/siglens/siglens/pkg/segment" 29 "github.com/siglens/siglens/pkg/segment/structs" 30 segutils "github.com/siglens/siglens/pkg/segment/utils" 31 segwriter "github.com/siglens/siglens/pkg/segment/writer" 32 "github.com/siglens/siglens/pkg/utils" 33 vtable "github.com/siglens/siglens/pkg/virtualtable" 34 log "github.com/sirupsen/logrus" 35 "github.com/valyala/fasthttp" 36 ) 37 38 var ScrollLimit uint64 = 10000 39 40 func ProcessSearchRequest(ctx *fasthttp.RequestCtx, myid uint64) { 41 var httpResp utils.HttpServerESResponseOuter 42 var httpRespScroll utils.HttpServerESResponseScroll 43 queryStart := time.Now() 44 45 queryJson := ProcessHttpGetRequest(ctx) 46 queryArgs := ctx.QueryArgs() 47 scrollTimeout := queryArgs.Peek("scroll") 48 getTotalHits, err := strconv.ParseBool(string(queryArgs.Peek("rest_total_hits_as_int"))) 49 if err != nil { 50 getTotalHits = false 51 } 52 53 indexNameUrl := utils.ExtractParamAsString(ctx.UserValue("indexName")) 54 indexNameIn, err := url.QueryUnescape(indexNameUrl) 55 if err != nil { 56 log.Errorf("ProcessSearchRequest: could not decode indexNameUrl=%v, err=%v", indexNameUrl, err) 57 var httpResp utils.HttpServerResponse 58 ctx.SetStatusCode(fasthttp.StatusBadRequest) 59 httpResp.Message = "Bad Request" 60 httpResp.StatusCode = fasthttp.StatusBadRequest 61 utils.WriteResponse(ctx, httpResp) 62 return 63 } 64 65 if indexNameIn == "" { 66 log.Infof("ProcessSearchRequest: No index name provided. Retrieving all index names") 67 indexNameIn = "*" 68 } 69 70 ti := structs.InitTableInfo(indexNameIn, myid, true) 71 isJaegerQuery := false 72 for _, indexName := range ti.GetQueryTables() { 73 if strings.HasPrefix(indexName, "jaeger-") { 74 isJaegerQuery = true 75 } else { 76 isJaegerQuery = false 77 break 78 } 79 } 80 81 qid := rutils.GetNextQid() 82 log.Infof("qid=%v, esQueryHandler: tableInfo=[%v], queryJson=[%v] scroll = [%v]", 83 qid, ti.String(), string(queryJson), string(scrollTimeout)) 84 85 requestURI := ctx.URI().String() 86 var simpleNode *structs.ASTNode 87 var aggs *structs.QueryAggregators 88 var sizeLimit uint64 89 var scrollRecord *scroll.Scroll 90 if strings.Contains(requestURI, "_opendistro") { 91 simpleNode, aggs, sizeLimit, scrollRecord, err = query.ParseOpenDistroRequest(queryJson, qid, isJaegerQuery, string(scrollTimeout)) 92 } else { 93 simpleNode, aggs, sizeLimit, scrollRecord, err = query.ParseRequest(queryJson, qid, isJaegerQuery, string(scrollTimeout)) 94 } 95 if err != nil { 96 ctx.SetStatusCode(fasthttp.StatusBadRequest) 97 _, err = ctx.WriteString(err.Error()) 98 if err != nil { 99 log.Errorf("qid=%v, esQueryHandler: could not write error message err=%v", qid, err) 100 } 101 log.Errorf("qid=%v, esQueryHandler: Error parsing query err=%+v", qid, err) 102 return 103 } 104 105 aggs.EarlyExit = !getTotalHits // if we should get total hits, don't early exit 106 if specialQuery, aggName := isAllIndexAggregationQuery(simpleNode, aggs, qid); specialQuery { 107 log.Infof("qid=%d, ProcessSearchRequest: Processing special query for only index name aggregations.", qid) 108 res := getIndexNameAggOnly(aggName, myid) 109 httpResp = query.GetQueryResponseJson(res, indexNameIn, queryStart, sizeLimit, qid, aggs) 110 utils.WriteJsonResponse(ctx, httpResp) 111 ctx.SetStatusCode(fasthttp.StatusOK) 112 return 113 } 114 115 if simpleNode == nil && aggs == nil && scrollRecord == nil { 116 ctx.SetStatusCode(fasthttp.StatusBadRequest) 117 _, err = ctx.WriteString("Failed to parse query!") 118 if err != nil { 119 log.Errorf("qid=%v, esQueryHandler: could not write error message err=%v", qid, err) 120 } 121 log.Errorf("qid=%v, esQueryHandler: Failed to parse query, simpleNode=%v, aggs=%v, err=%v", qid, simpleNode, aggs, err) 122 return 123 } 124 125 if scrollRecord != nil && !scroll.IsScrollIdValid(scrollRecord.Scroll_id) { 126 ctx.SetStatusCode(fasthttp.StatusBadRequest) 127 _, err = ctx.WriteString("Scroll Timeout : Invalid Search context") 128 if err != nil { 129 log.Errorf("qid=%v, esQueryHandler: could not write error message err=%v", qid, err) 130 } 131 log.Errorf("qid=%v, esQueryHandler: Scroll Timeout %v : Invalid Search context", qid, scrollRecord.Scroll_id) 132 return 133 } 134 135 if simpleNode == nil && scrollRecord == nil { 136 // we construct a "match_all" node 137 simpleNode, _ = query.GetMatchAllASTNode(qid) 138 } 139 segment.LogASTNode("ProcessSearchRequest", simpleNode, qid) 140 segment.LogQueryAggsNode("ProcessSearchRequest", aggs, qid) 141 log.Infof("qid=%v, esQueryHandler: indexNameIn=[%v], queryJson=[%v] scroll = [%v]", 142 qid, indexNameIn, string(queryJson), string(scrollTimeout)) 143 144 if scrollRecord != nil { 145 if !scroll.IsScrollIdValid(scrollRecord.Scroll_id) { 146 ctx.SetStatusCode(fasthttp.StatusBadRequest) 147 _, err = ctx.WriteString("Scroll Timeout : Invalid Search context") 148 if err != nil { 149 log.Errorf("qid=%v, esQueryHandler: could not write error message err=%v", qid, err) 150 } 151 log.Errorf("qid=%v, esQueryHandler: Scroll Timeout %v : Invalid Search context", qid, scrollRecord.Scroll_id) 152 return 153 } else { 154 ctx.SetStatusCode(fasthttp.StatusOK) 155 ctx.Response.Header.Set("Content-Type", "application/json") 156 if scrollRecord.Results == nil { 157 qc := structs.InitQueryContextWithTableInfo(ti, ScrollLimit, 0, myid, true) 158 segment.LogQueryContext(qc, qid) 159 result := segment.ExecuteQuery(simpleNode, aggs, qid, qc) 160 httpRespOuter := query.GetQueryResponseJson(result, indexNameIn, queryStart, sizeLimit, qid, aggs) 161 scrollRecord.Results = &httpRespOuter 162 } 163 httpRespScroll = query.GetQueryResponseJsonScroll(indexNameIn, queryStart, sizeLimit, scrollRecord, qid) 164 utils.WriteJsonResponse(ctx, httpRespScroll) 165 } 166 } else { 167 qc := structs.InitQueryContextWithTableInfo(ti, sizeLimit, 0, myid, true) 168 result := segment.ExecuteQuery(simpleNode, aggs, qid, qc) 169 httpResp = query.GetQueryResponseJson(result, indexNameIn, queryStart, sizeLimit, qid, aggs) 170 utils.WriteJsonResponse(ctx, httpResp) 171 } 172 ctx.SetStatusCode(fasthttp.StatusOK) 173 } 174 175 func ProcessHttpGetRequest(ctx *fasthttp.RequestCtx) []byte { 176 var httpResp utils.HttpServerResponse 177 queryJson := ctx.PostBody() 178 if queryJson == nil { 179 ctx.SetStatusCode(fasthttp.StatusBadRequest) 180 httpResp.Message = "Bad request" 181 httpResp.StatusCode = fasthttp.StatusBadRequest 182 utils.WriteResponse(ctx, httpResp) 183 return queryJson 184 } 185 return queryJson 186 } 187 188 /* 189 Uses microreader & segwriter to get the doc counts per index name 190 191 TODO: how does this look in a multi node setting? 192 Returns NodeResults with doc counts per index aggregation 193 */ 194 func getIndexNameAggOnly(aggName string, myid uint64) *structs.NodeResult { 195 196 allVirtualTableNames, err := vtable.GetVirtualTableNames(myid) 197 if err != nil { 198 return &structs.NodeResult{ErrList: []error{err}} 199 } 200 201 totalHits := uint64(0) 202 bucketResults := make([]*structs.BucketResult, 0) 203 for indexName := range allVirtualTableNames { 204 if indexName == "" { 205 log.Errorf("getIndexNameAggOnly: skipping an empty index name indexName=%v", indexName) 206 continue 207 } 208 _, eventCount, _ := segwriter.GetVTableCounts(indexName, myid) 209 _, unrotatedEventCount, _ := segwriter.GetUnrotatedVTableCounts(indexName, myid) 210 totalEventsForIndex := uint64(eventCount) + uint64(unrotatedEventCount) 211 totalHits += totalEventsForIndex 212 currBucket := &structs.BucketResult{ 213 ElemCount: totalEventsForIndex, 214 BucketKey: indexName, 215 } 216 bucketResults = append(bucketResults, currBucket) 217 } 218 aggResult := make(map[string]*structs.AggregationResult) 219 aggResult[aggName] = &structs.AggregationResult{ 220 IsDateHistogram: false, 221 Results: bucketResults, 222 } 223 224 return &structs.NodeResult{ 225 AllRecords: make([]*segutils.RecordResultContainer, 0), 226 Histogram: aggResult, 227 TotalResults: &structs.QueryCount{TotalCount: totalHits, Op: segutils.Equals}, 228 SegEncToKey: make(map[uint16]string), 229 } 230 }