github.com/siglens/siglens@v0.0.0-20240328180423-f7ce9ae441ed/pkg/es/query/queryresponse.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 query
    18  
    19  import (
    20  	"fmt"
    21  	"time"
    22  
    23  	"github.com/nqd/flat"
    24  	"github.com/siglens/siglens/pkg/scroll"
    25  	"github.com/siglens/siglens/pkg/segment/reader/record"
    26  	"github.com/siglens/siglens/pkg/segment/structs"
    27  	"github.com/valyala/fasthttp"
    28  
    29  	"github.com/siglens/siglens/pkg/utils"
    30  	log "github.com/sirupsen/logrus"
    31  )
    32  
    33  func checkScrollSize(offset uint64, size uint64, total uint64) uint64 {
    34  	if offset+size >= total {
    35  		size = total - offset
    36  	}
    37  	return size
    38  }
    39  
    40  func GetQueryResponseJsonScroll(indexName string, queryStart time.Time, sizeLimit uint64,
    41  	scrollRecord *scroll.Scroll, qid uint64) utils.HttpServerESResponseScroll {
    42  	var httpRespOuter utils.HttpServerESResponseOuter
    43  	var httpResp utils.HttpServerESResponseScroll
    44  	var subset []utils.Hits
    45  	if scroll.IsScrollIdValid(scrollRecord.Scroll_id) {
    46  		resultLen := scrollRecord.Results.Hits.GetHits()
    47  		scrollRecord.Size = checkScrollSize(scrollRecord.Offset, scrollRecord.Size, resultLen)
    48  		subset = scrollRecord.Results.Hits.Hits[scrollRecord.Offset : scrollRecord.Offset+scrollRecord.Size]
    49  		scrollRecord.Offset = scrollRecord.Offset + scrollRecord.Size
    50  		scroll.SetScrollRecord(scrollRecord.Scroll_id, scrollRecord)
    51  		err := scrollRecord.FlushScrollContextToFile()
    52  		if err != nil {
    53  			log.Errorf("qid=%d, GetQueryResponseJsonScroll: error flushing scroll result for id %+v Err: %v", qid, scrollRecord.Scroll_id, err)
    54  		}
    55  		if len(subset) > 0 {
    56  			err := scrollRecord.WriteScrollResultToFile()
    57  			if err != nil {
    58  				log.Errorf("qid=%d, GetQueryResponseJsonScroll: error writing scroll result %v", qid, err)
    59  			}
    60  		}
    61  		httpResp.Hits.Hits = subset
    62  		httpResp.Took = time.Since(queryStart).Milliseconds()
    63  		log.Infof("qid=%d, Scroll Query Took %+v ms", qid, httpRespOuter.Took)
    64  		httpResp.Hits.Total = scrollRecord.Results.Hits.Total
    65  		httpResp.Timed_out = false
    66  		httpResp.StatusCode = 200
    67  		httpResp.Scroll_id = scrollRecord.Scroll_id
    68  	} else {
    69  		httpResp.StatusCode = fasthttp.StatusBadRequest
    70  		httpResp.Hits.Hits = []utils.Hits{}
    71  		log.Errorf("qid=%d, Scroll Timeout %v : Invalid Search context", qid, scrollRecord.Scroll_id)
    72  	}
    73  	return httpResp
    74  
    75  }
    76  
    77  func GetQueryResponseJson(nodeResult *structs.NodeResult, indexName string, queryStart time.Time, sizeLimit uint64, qid uint64, aggs *structs.QueryAggregators) utils.HttpServerESResponseOuter {
    78  	var httpRespOuter utils.HttpServerESResponseOuter
    79  	var httpResp utils.HttpServerESResponse
    80  
    81  	// aggs exist, so just return aggregations instead of all results
    82  	httpRespOuter.Aggs = make(map[string]utils.BucketWrapper)
    83  	for aggName, aggRes := range nodeResult.Histogram {
    84  		allBuckets := make([]map[string]interface{}, len(aggRes.Results))
    85  		for idx, hist := range aggRes.Results {
    86  			res := make(map[string]interface{})
    87  			res["key"] = hist.BucketKey
    88  			res["doc_count"] = hist.ElemCount
    89  			if aggRes.IsDateHistogram {
    90  				res["key_as_string"] = fmt.Sprintf("%v", hist.BucketKey)
    91  			}
    92  			for name, value := range hist.StatRes {
    93  				res[name] = utils.StatResponse{
    94  					Value: value.CVal,
    95  				}
    96  			}
    97  
    98  			allBuckets[idx] = res
    99  		}
   100  		httpRespOuter.Aggs[aggName] = utils.BucketWrapper{Bucket: allBuckets}
   101  	}
   102  
   103  	if sizeLimit == 0 || len(nodeResult.AllRecords) == 0 {
   104  		httpResp.Hits = make([]utils.Hits, 0)
   105  	} else {
   106  		var _id string
   107  		allJsons, _, err := record.GetJsonFromAllRrc(nodeResult.AllRecords, true, qid, nodeResult.SegEncToKey, aggs)
   108  		if err != nil {
   109  			log.Errorf("qid=%d, GetQueryResponseJson: failed to get allrecords from rrc, err=%v", qid, err)
   110  			return httpRespOuter
   111  		}
   112  		for _, jsonSource := range allJsons {
   113  			if val, pres := jsonSource["_id"]; pres {
   114  				_id = val.(string)
   115  			} else {
   116  				_id = ""
   117  			}
   118  			var idxToPut string
   119  			if val, pres := jsonSource["_index"]; pres {
   120  				idxToPut = val.(string)
   121  			} else {
   122  				idxToPut = indexName
   123  			}
   124  			var docTypeToPut string
   125  			if val, pres := jsonSource["_type"]; pres {
   126  				docTypeToPut = val.(string)
   127  			} else {
   128  				docTypeToPut = "unknown"
   129  			}
   130  
   131  			finalSrc, err := flat.Unflatten(jsonSource, nil)
   132  			if err != nil {
   133  				log.Errorf("qid=%d, GetQueryResponseJson: Failed to unflatten, src=[%v], err=%v", qid, jsonSource, err)
   134  				return httpRespOuter
   135  			}
   136  
   137  			jsonMap := utils.Hits{Index: idxToPut, Type: docTypeToPut, Id: _id, Version: 1, Score: 1, Source: finalSrc}
   138  
   139  			httpResp.Hits = append(httpResp.Hits, jsonMap)
   140  		}
   141  	}
   142  
   143  	httpResp.Total = convertQueryCountToESResponse(nodeResult.TotalResults)
   144  	httpResp.Max_score = 0
   145  	httpRespOuter.Hits = httpResp
   146  	httpRespOuter.Took = time.Since(queryStart).Milliseconds()
   147  	log.Infof("qid=%d, Query Took %+v ms", qid, httpRespOuter.Took)
   148  
   149  	httpRespOuter.Timed_out = false
   150  	httpRespOuter.StatusCode = 200
   151  
   152  	shards := make(map[string]interface{})
   153  	shards["total"] = 1
   154  	shards["successful"] = 1
   155  	shards["skipped"] = 0
   156  	shards["failed"] = 0
   157  
   158  	httpRespOuter.Shards = shards
   159  	return httpRespOuter
   160  }
   161  
   162  func convertQueryCountToESResponse(qc *structs.QueryCount) interface{} {
   163  	if qc == nil {
   164  		return 0
   165  	}
   166  
   167  	if !qc.EarlyExit {
   168  		return qc.TotalCount
   169  	}
   170  
   171  	return utils.HitsCount{Value: qc.TotalCount, Relation: qc.Op.ToString()}
   172  }