github.com/siglens/siglens@v0.0.0-20240328180423-f7ce9ae441ed/pkg/segment/query/segqueryhelpers.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  
    22  	dtu "github.com/siglens/siglens/pkg/common/dtypeutils"
    23  	"github.com/siglens/siglens/pkg/config"
    24  	"github.com/siglens/siglens/pkg/querytracker"
    25  	"github.com/siglens/siglens/pkg/segment/query/summary"
    26  	"github.com/siglens/siglens/pkg/segment/search"
    27  	"github.com/siglens/siglens/pkg/segment/structs"
    28  	"github.com/siglens/siglens/pkg/segment/utils"
    29  	log "github.com/sirupsen/logrus"
    30  )
    31  
    32  // Holder struct for all query information
    33  type queryInformation struct {
    34  	sNode              *structs.SearchNode
    35  	aggs               *structs.QueryAggregators
    36  	queryRange         *dtu.TimeRange
    37  	colsToSearch       map[string]bool
    38  	indexInfo          *structs.TableInfo
    39  	sizeLimit          uint64
    40  	pqid               string
    41  	parallelismPerFile int64
    42  	dqs                *DistributedQueryService
    43  	persistentQuery    bool
    44  	qid                uint64
    45  	sNodeType          structs.SearchNodeType
    46  	qType              structs.QueryType
    47  	orgId              uint64
    48  }
    49  
    50  type querySegmentRequest struct {
    51  	queryInformation
    52  	segKey        string
    53  	segKeyTsRange *dtu.TimeRange
    54  	tableName     string
    55  	sType         structs.SegType
    56  	blkTracker    *structs.BlockTracker
    57  	HasMatchedRrc bool
    58  }
    59  
    60  /*
    61  Returns a holder struct with query information
    62  
    63  # This contains DistributedQueryService, which will be used to send grpcs to other nodes as needed
    64  
    65  The caller is responsible for calling qs.Wait() to wait for all grpcs to finish
    66  */
    67  func InitQueryInformation(s *structs.SearchNode, aggs *structs.QueryAggregators, queryRange *dtu.TimeRange,
    68  	indexInfo *structs.TableInfo, sizeLimit uint64, parallelismPerFile int64, qid uint64,
    69  	dqs *DistributedQueryService, orgid uint64) (*queryInformation, error) {
    70  	colsToSearch, _, _ := search.GetAggColsAndTimestamp(aggs)
    71  	isQueryPersistent, err := querytracker.IsQueryPersistent(indexInfo.GetQueryTables(), s)
    72  	if err != nil {
    73  		log.Errorf("InitQueryInformation: failed to check if query is persistent! Err %v", err)
    74  		return &queryInformation{}, err
    75  	}
    76  	pqid := querytracker.GetHashForQuery(s)
    77  	sNodeType, qType := getQueryType(s, aggs)
    78  	return &queryInformation{
    79  		sNode:              s,
    80  		aggs:               aggs,
    81  		queryRange:         queryRange,
    82  		colsToSearch:       colsToSearch,
    83  		indexInfo:          indexInfo,
    84  		sizeLimit:          sizeLimit,
    85  		pqid:               pqid,
    86  		parallelismPerFile: parallelismPerFile,
    87  		dqs:                dqs,
    88  		persistentQuery:    isQueryPersistent,
    89  		qid:                qid,
    90  		sNodeType:          sNodeType,
    91  		qType:              qType,
    92  		orgId:              orgid,
    93  	}, nil
    94  }
    95  
    96  // waits and closes the distributed query service
    97  func (qi *queryInformation) Wait(querySummary *summary.QuerySummary) error {
    98  	return qi.dqs.Wait(qi.qid, querySummary)
    99  }
   100  
   101  // returns map[table] -> map[segKey] -> blkTracker to pass into MicroIndexCheck and ExtractSSRFromSearchNode
   102  // Returns error if qsr.blkTracker is nil
   103  func (qsr *querySegmentRequest) GetMicroIndexFilter() (map[string]map[string]*structs.BlockTracker, error) {
   104  	if qsr.blkTracker == nil {
   105  		log.Errorf("GetMicroIndexFilter: qsr.blkTracker is nil! Cannot construct keys & blocks to filter")
   106  		return nil, fmt.Errorf("GetMicroIndexFilter: qsr.blkTracker is nil! Cannot construct keys & blocks to filter")
   107  	}
   108  	retVal := make(map[string]map[string]*structs.BlockTracker)
   109  	retVal[qsr.tableName] = make(map[string]*structs.BlockTracker)
   110  	retVal[qsr.tableName][qsr.segKey] = qsr.blkTracker
   111  	return retVal, nil
   112  }
   113  
   114  // returns map[table] -> map[segKey] -> entire file block tracker to pass into MicroIndexCheck and ExtractSSRFromSearchNode
   115  func (qsr *querySegmentRequest) GetEntireFileMicroIndexFilter() map[string]map[string]*structs.BlockTracker {
   116  	retVal := make(map[string]map[string]*structs.BlockTracker)
   117  	retVal[qsr.tableName] = make(map[string]*structs.BlockTracker)
   118  	retVal[qsr.tableName][qsr.segKey] = structs.InitEntireFileBlockTracker()
   119  	return retVal
   120  }
   121  
   122  func ConvertASTNodeToSearchNode(node *structs.ASTNode, qid uint64) *structs.SearchNode {
   123  	currNode := &structs.SearchNode{}
   124  	if node.AndFilterCondition != nil {
   125  		currNode.AndSearchConditions = convertASTConditionToSearchCondition(node.AndFilterCondition, qid)
   126  	}
   127  
   128  	if node.OrFilterCondition != nil {
   129  		currNode.OrSearchConditions = convertASTConditionToSearchCondition(node.OrFilterCondition, qid)
   130  	}
   131  	// for exclusion, only join the column info for files that exist and not the actual search request info
   132  	// exclusion conditions should not influence raw blocks to search
   133  	if node.ExclusionFilterCondition != nil {
   134  		currNode.ExclusionSearchConditions = convertASTConditionToSearchCondition(node.ExclusionFilterCondition, qid)
   135  	}
   136  	currNode.AddQueryInfoForNode()
   137  	return currNode
   138  }
   139  
   140  func convertASTConditionToSearchCondition(condition *structs.Condition, qid uint64) *structs.SearchCondition {
   141  	currSearch := &structs.SearchCondition{}
   142  	if condition.FilterCriteria != nil && len(condition.FilterCriteria) > 0 {
   143  		currSearch.SearchQueries = convertFilterCriteraToSearchQuery(condition.FilterCriteria, qid)
   144  	}
   145  
   146  	if condition.NestedNodes != nil && len(condition.NestedNodes) > 0 {
   147  		for _, node := range condition.NestedNodes {
   148  			searchNodes := ConvertASTNodeToSearchNode(node, qid)
   149  
   150  			if currSearch.SearchNode == nil {
   151  				currSearch.SearchNode = make([]*structs.SearchNode, 0)
   152  			}
   153  			currSearch.SearchNode = append(currSearch.SearchNode, searchNodes)
   154  		}
   155  	}
   156  	return currSearch
   157  }
   158  
   159  func convertFilterCriteraToSearchQuery(conditions []*structs.FilterCriteria, qid uint64) []*structs.SearchQuery {
   160  	finalSearchQueries := make([]*structs.SearchQuery, 0)
   161  	for _, filter := range conditions {
   162  		currQuery := structs.GetSearchQueryFromFilterCriteria(filter, qid)
   163  		finalSearchQueries = append(finalSearchQueries, currQuery)
   164  	}
   165  	return finalSearchQueries
   166  }
   167  
   168  // put this in segwriter -> raw search unrotated
   169  func ExtractSSRFromSearchNode(node *structs.SearchNode, filesToSearch map[string]map[string]*structs.BlockTracker, timeRange *dtu.TimeRange,
   170  	indexNames []string, querySummary *summary.QuerySummary, qid uint64, isQueryPersistent bool, pqid string) map[string]*structs.SegmentSearchRequest {
   171  	// todo: better joining of intermediate results of block summaries
   172  	finalList := make(map[string]*structs.SegmentSearchRequest)
   173  	if node.AndSearchConditions != nil {
   174  		andSegmentFiles := extractSSRFromCondition(node.AndSearchConditions, utils.And,
   175  			filesToSearch, timeRange, indexNames, querySummary, qid, isQueryPersistent, pqid)
   176  		for fileName, searchReq := range andSegmentFiles {
   177  			if _, ok := finalList[fileName]; !ok {
   178  				finalList[fileName] = searchReq
   179  				continue
   180  			}
   181  			finalList[fileName].JoinRequest(searchReq, utils.And)
   182  		}
   183  	}
   184  
   185  	if node.OrSearchConditions != nil {
   186  		orSegmentFiles := extractSSRFromCondition(node.OrSearchConditions, utils.Or,
   187  			filesToSearch, timeRange, indexNames, querySummary, qid, isQueryPersistent, pqid)
   188  		for fileName, searchReq := range orSegmentFiles {
   189  			if _, ok := finalList[fileName]; !ok {
   190  				finalList[fileName] = searchReq
   191  				continue
   192  			}
   193  			finalList[fileName].JoinRequest(searchReq, utils.Or)
   194  		}
   195  	}
   196  	// for exclusion, only join the column info for files that exist and not the actual search request info
   197  	// exclusion conditions should not influence raw blocks to search
   198  	if node.ExclusionSearchConditions != nil {
   199  		exclustionSegmentFiles := extractSSRFromCondition(node.ExclusionSearchConditions, utils.And,
   200  			filesToSearch, timeRange, indexNames, querySummary, qid, isQueryPersistent, pqid)
   201  		for fileName, searchReq := range exclustionSegmentFiles {
   202  			if _, ok := finalList[fileName]; !ok {
   203  				continue
   204  			}
   205  			finalList[fileName].JoinColumnInfo(searchReq)
   206  		}
   207  	}
   208  
   209  	return finalList
   210  }
   211  
   212  func extractSSRFromCondition(condition *structs.SearchCondition, op utils.LogicalOperator, filesToSearch map[string]map[string]*structs.BlockTracker,
   213  	timeRange *dtu.TimeRange, indexNames []string, querySummary *summary.QuerySummary, qid uint64, isQueryPersistent bool, pqid string) map[string]*structs.SegmentSearchRequest {
   214  	finalSegFiles := make(map[string]*structs.SegmentSearchRequest)
   215  	if condition.SearchQueries != nil {
   216  		for _, query := range condition.SearchQueries {
   217  			segFiles, err := MicroIndexCheck(query, filesToSearch, timeRange, indexNames, querySummary, qid, isQueryPersistent, pqid)
   218  			if err != nil {
   219  				log.Errorf("qid=%d, error when checking micro indices: %+v", qid, err)
   220  			}
   221  			for fileName, searchReq := range segFiles {
   222  				if _, ok := finalSegFiles[fileName]; !ok {
   223  					finalSegFiles[fileName] = searchReq
   224  				} else {
   225  					finalSegFiles[fileName].JoinRequest(searchReq, op)
   226  				}
   227  			}
   228  		}
   229  	}
   230  
   231  	if condition.SearchNode != nil {
   232  		for _, node := range condition.SearchNode {
   233  			segmentFiles := ExtractSSRFromSearchNode(node, filesToSearch, timeRange, indexNames, querySummary, qid, isQueryPersistent, pqid)
   234  
   235  			for fileName, searchReq := range segmentFiles {
   236  				if _, ok := finalSegFiles[fileName]; !ok {
   237  					finalSegFiles[fileName] = searchReq
   238  					continue
   239  				}
   240  				finalSegFiles[fileName].JoinRequest(searchReq, op)
   241  			}
   242  		}
   243  	}
   244  	return finalSegFiles
   245  }
   246  
   247  // todo: better and more generic node types.
   248  // Right now, we just assume if its not ColumnValue, then it has to be TimeRangeQuery
   249  func GetNodeTypeFromNode(node *structs.SearchNode) structs.SearchNodeType {
   250  	var s structs.SearchNodeType
   251  	if node.AndSearchConditions != nil {
   252  		nodeType := GetNodeTypeFromCondition(node.AndSearchConditions)
   253  		if nodeType == structs.ColumnValueQuery {
   254  			return structs.ColumnValueQuery
   255  		}
   256  	}
   257  
   258  	if node.OrSearchConditions != nil {
   259  		nodeType := GetNodeTypeFromCondition(node.OrSearchConditions)
   260  		if nodeType == structs.ColumnValueQuery {
   261  			return structs.ColumnValueQuery
   262  		}
   263  	}
   264  
   265  	if node.ExclusionSearchConditions != nil {
   266  		nodeType := GetNodeTypeFromCondition(node.ExclusionSearchConditions)
   267  		if nodeType == structs.ColumnValueQuery {
   268  			return structs.ColumnValueQuery
   269  		}
   270  	}
   271  	return s
   272  }
   273  
   274  func GetNodeTypeFromCondition(searchCond *structs.SearchCondition) structs.SearchNodeType {
   275  	if searchCond.SearchNode != nil {
   276  		for _, search := range searchCond.SearchNode {
   277  			nodeType := GetNodeTypeFromNode(search)
   278  			if nodeType == structs.ColumnValueQuery {
   279  				return structs.ColumnValueQuery
   280  			}
   281  		}
   282  	}
   283  	if searchCond.SearchQueries != nil {
   284  		for _, search := range searchCond.SearchQueries {
   285  			nodeType := GetNodeTypeFromQuery(search)
   286  			if nodeType == structs.ColumnValueQuery {
   287  				return structs.ColumnValueQuery
   288  			}
   289  		}
   290  	}
   291  	return structs.MatchAllQuery
   292  }
   293  
   294  func GetNodeTypeFromQuery(query *structs.SearchQuery) structs.SearchNodeType {
   295  	if query.ExpressionFilter != nil {
   296  		if !query.ExpressionFilter.IsTimeRangeFilter() {
   297  			return structs.ColumnValueQuery
   298  		}
   299  	} else {
   300  		if query.MatchFilter.MatchColumn == "*" {
   301  			return structs.MatchAllQuery
   302  		}
   303  		if query.MatchFilter.MatchColumn != config.GetTimeStampKey() {
   304  			return structs.ColumnValueQuery
   305  		}
   306  	}
   307  	return structs.MatchAllQuery
   308  }