github.com/siglens/siglens@v0.0.0-20240328180423-f7ce9ae441ed/pkg/segment/results/blockresults/sortblockresult.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 blockresults
    18  
    19  import (
    20  	"container/heap"
    21  	"errors"
    22  
    23  	"github.com/siglens/siglens/pkg/segment/structs"
    24  	"github.com/siglens/siglens/pkg/segment/utils"
    25  )
    26  
    27  type SortResults struct {
    28  	Results   *SortedResultRecords
    29  	Ascending bool                 // ascending or descending order
    30  	Count     uint64               // number of results
    31  	LastValue float64              // value of the record to be replaced (ex. the highest min, the lowest max for Count elems)
    32  	Sort      *structs.SortRequest // request for aggregations
    33  }
    34  
    35  func GetRrsFromRrc(rec *utils.RecordResultContainer, sortReq *structs.SortRequest) *ResultRecordSort {
    36  
    37  	currRrs := &ResultRecordSort{
    38  		Rrc:       rec,
    39  		Ascending: sortReq.Ascending,
    40  	}
    41  	return currRrs
    42  }
    43  
    44  func InitializeSort(count uint64, sort *structs.SortRequest) (*SortResults, error) {
    45  	if sort == nil {
    46  		return nil, errors.New("received a sort request with no aggregations")
    47  	}
    48  	var sortedResRecs SortedResultRecords
    49  	heap.Init(&sortedResRecs)
    50  	return &SortResults{
    51  		Results:   &sortedResRecs,
    52  		Count:     count,
    53  		Ascending: sort.Ascending,
    54  		Sort:      sort,
    55  	}, nil
    56  }
    57  
    58  /*
    59  Returns:
    60    - bool if this record was added
    61    - string for any remote records that were removed
    62  */
    63  func (s *SortResults) Add(rrc *utils.RecordResultContainer) (bool, string) {
    64  
    65  	// first record seen
    66  	if currRes := uint64(s.Results.Len()); currRes == 0 {
    67  		sortedRec := GetRrsFromRrc(rrc, s.Sort)
    68  		heap.Push(s.Results, sortedRec)
    69  		s.LastValue = rrc.SortColumnValue
    70  		return true, ""
    71  	} else if currRes < s.Count {
    72  		// less than Count records seen
    73  		sortedRec := GetRrsFromRrc(rrc, s.Sort)
    74  		heap.Push(s.Results, sortedRec)
    75  		if s.Ascending { // last value should be largest value (first 'min' to be replaced)
    76  			if rrc.SortColumnValue > s.LastValue {
    77  				s.LastValue = rrc.SortColumnValue
    78  			}
    79  		} else { // last value should be smallest value (first 'max' to be replaced)
    80  			if rrc.SortColumnValue < s.LastValue {
    81  				s.LastValue = rrc.SortColumnValue
    82  			}
    83  		}
    84  		return true, ""
    85  	} else { // heap has Count elements
    86  		if s.ShouldAddRecord(rrc) {
    87  			sortedRec := GetRrsFromRrc(rrc, s.Sort)
    88  			heap.Push(s.Results, sortedRec)
    89  			removedVal := s.UpdateLastValue()
    90  			return true, removedVal
    91  		}
    92  	}
    93  	return false, ""
    94  }
    95  
    96  func (s *SortResults) ShouldAddRecord(rrc *utils.RecordResultContainer) bool {
    97  	if s.Ascending {
    98  		if rrc.SortColumnValue < s.LastValue {
    99  			return true
   100  		}
   101  	} else {
   102  		if rrc.SortColumnValue > s.LastValue {
   103  			return true
   104  		}
   105  	}
   106  	return false
   107  }
   108  
   109  /*
   110  		Returns:
   111  			- string for any records that were removed
   112  
   113  	 TODO: be smarter about updating and removing last value
   114  */
   115  func (s *SortResults) UpdateLastValue() string {
   116  
   117  	idx := 0
   118  	var found bool
   119  	var removed string // id of the record that was removed
   120  	if !s.Ascending {
   121  		for i := int(s.Count) - 1; i >= 0; i-- {
   122  			if (*s.Results)[i].Rrc.SortColumnValue == s.LastValue {
   123  				idx = i
   124  				found = true
   125  				removed = (*s.Results)[i].Rrc.SegKeyInfo.RecordId
   126  				break
   127  			}
   128  		}
   129  	} else {
   130  		for i := 0; i < int(s.Count); i++ {
   131  			if (*s.Results)[i].Rrc.SortColumnValue == s.LastValue {
   132  				idx = i
   133  				found = true
   134  				removed = (*s.Results)[i].Rrc.SegKeyInfo.RecordId
   135  				break
   136  			}
   137  		}
   138  	}
   139  
   140  	if found {
   141  		heap.Remove(s.Results, idx)
   142  	}
   143  	s.LastValue = s.GetLastValue()
   144  	return removed
   145  }
   146  
   147  func (s *SortResults) GetLastValue() float64 {
   148  	var lastVal float64
   149  
   150  	if !s.Ascending {
   151  		if s.Count > 0 {
   152  			lastVal = (*s.Results)[s.Count-1].Rrc.SortColumnValue
   153  		}
   154  		for i := int(s.Count) - 2; i >= 0; i-- {
   155  			if (*s.Results)[i].Rrc.SortColumnValue < lastVal {
   156  				lastVal = (*s.Results)[i].Rrc.SortColumnValue
   157  			}
   158  		}
   159  	} else {
   160  		if s.Count > 0 {
   161  			lastVal = (*s.Results)[0].Rrc.SortColumnValue
   162  		}
   163  		for i := uint64(1); i < s.Count; i++ {
   164  			if (*s.Results)[i].Rrc.SortColumnValue > lastVal {
   165  				lastVal = (*s.Results)[i].Rrc.SortColumnValue
   166  			}
   167  		}
   168  	}
   169  
   170  	return lastVal
   171  }
   172  
   173  // This function uses heap.Pop, therefore can only be called once
   174  func (s *SortResults) GetSortedResults() []*utils.RecordResultContainer {
   175  	size := uint64(s.Results.Len())
   176  	if s.Count < size {
   177  		size = s.Count
   178  	}
   179  
   180  	allSorts := make([]*utils.RecordResultContainer, size)
   181  	resultIdx := uint64(0)
   182  	for s.Results.Len() > 0 && resultIdx < s.Count {
   183  		item := heap.Pop(s.Results).(*ResultRecordSort)
   184  		allSorts[resultIdx] = item.Rrc
   185  		resultIdx++
   186  	}
   187  	allSorts = allSorts[:resultIdx]
   188  	return allSorts
   189  }
   190  
   191  func (s *SortResults) GetSortedResultsCopy() []*utils.RecordResultContainer {
   192  	size := uint64(s.Results.Len())
   193  	if s.Count < size {
   194  		size = s.Count
   195  	}
   196  
   197  	allSorts := make([]*utils.RecordResultContainer, size)
   198  	resultIdx := uint64(0)
   199  
   200  	var newHeap SortedResultRecords
   201  	heap.Init(&newHeap)
   202  	for s.Results.Len() > 0 && resultIdx < s.Count {
   203  		item := heap.Pop(s.Results).(*ResultRecordSort)
   204  		allSorts[resultIdx] = item.Rrc
   205  		resultIdx++
   206  		newHeap.Push(item)
   207  	}
   208  	allSorts = allSorts[:resultIdx]
   209  	s.Results = &newHeap
   210  	return allSorts
   211  }