zotregistry.dev/zot@v1.4.4-0.20240314164342-eec277e14d20/pkg/extensions/search/pagination/image_pagination.go (about) 1 package pagination 2 3 import ( 4 "fmt" 5 "sort" 6 "time" 7 8 zerr "zotregistry.dev/zot/errors" 9 zcommon "zotregistry.dev/zot/pkg/common" 10 gql_gen "zotregistry.dev/zot/pkg/extensions/search/gql_generated" 11 ) 12 13 type ImageSummariesPageFinder struct { 14 limit int 15 offset int 16 sortBy SortCriteria 17 pageBuffer []*gql_gen.ImageSummary 18 } 19 20 func NewImgSumPageFinder(limit, offset int, sortBy SortCriteria) (*ImageSummariesPageFinder, error) { 21 if sortBy == "" { 22 sortBy = AlphabeticAsc 23 } 24 25 if limit < 0 { 26 return nil, zerr.ErrLimitIsNegative 27 } 28 29 if offset < 0 { 30 return nil, zerr.ErrOffsetIsNegative 31 } 32 33 if _, found := ImgSumSortFuncs()[sortBy]; !found { 34 return nil, fmt.Errorf("sorting repos by '%s' is not supported %w", 35 sortBy, zerr.ErrSortCriteriaNotSupported) 36 } 37 38 return &ImageSummariesPageFinder{ 39 limit: limit, 40 offset: offset, 41 sortBy: sortBy, 42 pageBuffer: []*gql_gen.ImageSummary{}, 43 }, nil 44 } 45 46 func (pf *ImageSummariesPageFinder) Add(imgSum *gql_gen.ImageSummary) { 47 pf.pageBuffer = append(pf.pageBuffer, imgSum) 48 } 49 50 func (pf *ImageSummariesPageFinder) Page() ([]*gql_gen.ImageSummary, zcommon.PageInfo) { 51 if len(pf.pageBuffer) == 0 { 52 return []*gql_gen.ImageSummary{}, zcommon.PageInfo{} 53 } 54 55 pageInfo := zcommon.PageInfo{} 56 57 sort.Slice(pf.pageBuffer, ImgSumSortFuncs()[pf.sortBy](pf.pageBuffer)) 58 59 // the offset and limit are calculated in terms of repos counted 60 start := pf.offset 61 end := pf.offset + pf.limit 62 63 // we'll return an empty array when the offset is greater than the number of elements 64 if start >= len(pf.pageBuffer) { 65 start = len(pf.pageBuffer) 66 end = start 67 } 68 69 if end >= len(pf.pageBuffer) { 70 end = len(pf.pageBuffer) 71 } 72 73 page := pf.pageBuffer[start:end] 74 75 pageInfo.ItemCount = len(page) 76 77 if start == 0 && end == 0 { 78 page = pf.pageBuffer 79 pageInfo.ItemCount = len(page) 80 } 81 82 pageInfo.TotalCount = len(pf.pageBuffer) 83 84 return page, pageInfo 85 } 86 87 func ImgSumSortFuncs() map[SortCriteria]func(pageBuffer []*gql_gen.ImageSummary) func(i, j int) bool { 88 return map[SortCriteria]func(pageBuffer []*gql_gen.ImageSummary) func(i, j int) bool{ 89 AlphabeticAsc: ImgSortByAlphabeticAsc, 90 AlphabeticDsc: ImgSortByAlphabeticDsc, 91 UpdateTime: ImgSortByUpdateTime, 92 Relevance: ImgSortByRelevance, 93 Downloads: ImgSortByDownloads, 94 } 95 } 96 97 func ImgSortByAlphabeticAsc(pageBuffer []*gql_gen.ImageSummary) func(i, j int) bool { 98 return func(i, j int) bool { //nolint: varnamelen 99 if *pageBuffer[i].RepoName < *pageBuffer[j].RepoName { 100 return true 101 } 102 103 if *pageBuffer[i].RepoName == *pageBuffer[j].RepoName { 104 return *pageBuffer[i].Tag < *pageBuffer[j].Tag 105 } 106 107 return false 108 } 109 } 110 111 func ImgSortByAlphabeticDsc(pageBuffer []*gql_gen.ImageSummary) func(i, j int) bool { 112 return func(i, j int) bool { //nolint: varnamelen 113 if *pageBuffer[i].RepoName > *pageBuffer[j].RepoName { 114 return true 115 } 116 117 if *pageBuffer[i].RepoName == *pageBuffer[j].RepoName { 118 return *pageBuffer[i].Tag > *pageBuffer[j].Tag 119 } 120 121 return false 122 } 123 } 124 125 func ImgSortByRelevance(pageBuffer []*gql_gen.ImageSummary) func(i, j int) bool { 126 return func(i, j int) bool { //nolint: varnamelen 127 if *pageBuffer[i].RepoName < *pageBuffer[j].RepoName { 128 return true 129 } 130 131 if *pageBuffer[i].RepoName == *pageBuffer[j].RepoName { 132 return *pageBuffer[i].Tag < *pageBuffer[j].Tag 133 } 134 135 return false 136 } 137 } 138 139 // SortByUpdateTime sorting descending by time. 140 func ImgSortByUpdateTime(pageBuffer []*gql_gen.ImageSummary) func(i, j int) bool { 141 repos2LastUpdated := map[string]time.Time{} 142 143 for _, img := range pageBuffer { 144 lastUpdated, ok := repos2LastUpdated[*img.RepoName] 145 146 if !ok || lastUpdated.Before(*img.LastUpdated) { 147 repos2LastUpdated[*img.RepoName] = *img.LastUpdated 148 } 149 } 150 151 return func(i, j int) bool { 152 iRepoTime, jRepoTime := repos2LastUpdated[*pageBuffer[i].RepoName], repos2LastUpdated[*pageBuffer[j].RepoName] 153 154 return (iRepoTime.After(jRepoTime) || iRepoTime.Equal(jRepoTime)) && 155 pageBuffer[i].LastUpdated.After(*pageBuffer[j].LastUpdated) 156 } 157 } 158 159 // SortByDownloads returns a comparison function for descendant sorting by downloads. 160 func ImgSortByDownloads(pageBuffer []*gql_gen.ImageSummary) func(i, j int) bool { 161 return func(i, j int) bool { 162 return *pageBuffer[i].DownloadCount > *pageBuffer[j].DownloadCount 163 } 164 }