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  }