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 }