github.com/thanos-io/thanos@v0.32.5/pkg/ui/react-app/src/thanos/pages/blocks/helpers.ts (about) 1 import { LabelSet, Block, BlocksPool } from './block'; 2 import { Fuzzy, FuzzyResult } from '@nexucis/fuzzy'; 3 4 const stringify = (map: LabelSet): string => { 5 let t = ''; 6 for (const [key, value] of Object.entries(map)) { 7 t += `${key}: ${value} `; 8 } 9 return t; 10 }; 11 12 export const isOverlapping = (a: Block, b: Block): boolean => { 13 if (a?.minTime <= b?.minTime) return b?.minTime < a?.maxTime; 14 else return a?.minTime < b?.maxTime; 15 }; 16 17 const determineRow = (block: Block, rows: Block[][], startWithRow: number): number => { 18 if (rows.length === 0) return 0; 19 20 const len = rows[startWithRow]?.length || 0; 21 if (len === 0) return startWithRow; 22 23 if (isOverlapping(rows[startWithRow][len - 1], block)) { 24 // Blocks are overlapping, try next row. 25 return determineRow(block, rows, startWithRow + 1); 26 } 27 return startWithRow; 28 }; 29 30 const splitOverlappingBlocks = (blocks: Block[]): Block[][] => { 31 const rows: Block[][] = [[]]; 32 if (blocks.length === 0) return rows; 33 34 blocks.forEach((b) => { 35 const r = determineRow(b, rows, 0); 36 if (!rows[r]) rows[r] = []; 37 rows[r].push(b); 38 }); 39 return rows; 40 }; 41 42 const sortBlocksInRows = (blocks: Block[], findOverlappingBlocks: boolean): BlocksPool => { 43 const poolWithOverlaps: { [key: string]: Block[] } = {}; 44 45 blocks 46 .sort((a, b) => { 47 if (a.compaction.level - b.compaction.level) { 48 return a.compaction.level - b.compaction.level; 49 } 50 if (a.thanos.downsample.resolution - b.thanos.downsample.resolution) { 51 return a.thanos.downsample.resolution - b.thanos.downsample.resolution; 52 } 53 return a.minTime - b.minTime; 54 }) 55 .forEach((b) => { 56 const key = `${b.compaction.level}-${b.thanos.downsample.resolution}`; 57 if (!poolWithOverlaps[key]) poolWithOverlaps[key] = []; 58 59 poolWithOverlaps[key].push(b); 60 }); 61 62 const pool: BlocksPool = {}; 63 64 Object.entries(poolWithOverlaps).forEach(([key, blks]) => { 65 if (findOverlappingBlocks) { 66 let maxTime = 0; 67 const filteredOverlap = blks.filter((value, index) => { 68 const isOverlap = maxTime > value.minTime; 69 if (value.maxTime > maxTime) { 70 maxTime = value.maxTime; 71 } 72 return isOverlap || isOverlapping(blks[index], blks[index + 1]); 73 }); 74 pool[key] = splitOverlappingBlocks(filteredOverlap); 75 } else { 76 pool[key] = splitOverlappingBlocks(blks); 77 } 78 }); 79 80 return pool; 81 }; 82 83 export const sortBlocks = ( 84 blocks: Block[], 85 label: string, 86 findOverlappingBlocks: boolean 87 ): { [source: string]: BlocksPool } => { 88 const titles: { [key: string]: string } = {}; 89 const pool: { [key: string]: Block[] } = {}; 90 91 blocks 92 .sort((a, b) => a.compaction.level - b.compaction.level) 93 .forEach((b) => { 94 const title = (function (): string { 95 const key = label !== '' && b.thanos.labels[label]; 96 97 if (key) { 98 return key; 99 } else { 100 let t = titles[stringify(b.thanos.labels)]; 101 if (t === undefined) { 102 t = String(Object.keys(titles).length + 1); 103 titles[stringify(b.thanos.labels)] = t; 104 } 105 return t; 106 } 107 })(); 108 109 pool[title] = pool[title] ? pool[title].concat([b]) : [b]; 110 }); 111 112 const sortedPool: { [source: string]: BlocksPool } = {}; 113 Object.keys(pool).forEach((k) => { 114 sortedPool[k] = sortBlocksInRows(pool[k], findOverlappingBlocks); 115 }); 116 return sortedPool; 117 }; 118 119 export const download = (blob: Block): string => { 120 const url = window.URL.createObjectURL(new Blob([JSON.stringify(blob, null, 2)], { type: 'application/json' })); 121 122 return url; 123 }; 124 125 export const getBlockByUlid = (blocks: Block[], ulid: string): Block[] => { 126 if (ulid === '') { 127 return blocks; 128 } 129 130 const ulidArray = blocks.map((block) => block.ulid); 131 const fuz = new Fuzzy({ caseSensitive: true }); 132 133 const result: FuzzyResult[] = fuz.filter(ulid, ulidArray); 134 135 const resultIndex = result.map((value) => value.index); 136 137 const blockResult = blocks.filter((block, index) => resultIndex.includes(index)); 138 return blockResult; 139 }; 140 141 export const getBlocksByCompactionLevel = (blocks: Block[], compactionLevel: number): Block[] => { 142 if (compactionLevel === 0 || Number.isNaN(compactionLevel)) { 143 return blocks; 144 } 145 146 const blockResult = blocks.filter((block) => block.compaction.level === compactionLevel); 147 return blockResult; 148 }; 149 150 export const getFilteredBlockPools = ( 151 blockPools: { [source: string]: BlocksPool }, 152 filteredBlocks: Block[] 153 ): { [source: string]: BlocksPool } => { 154 const newblockPools: { [source: string]: BlocksPool } = {}; 155 Object.keys(blockPools).map((key: string) => { 156 const poolArrayIndex = blockPools[key]; 157 const poolArray = poolArrayIndex[Object.keys(poolArrayIndex)[0]]; 158 for (let i = 0; i < filteredBlocks.length; i++) { 159 if (JSON.stringify(filteredBlocks[i].thanos.labels) === JSON.stringify(poolArray[0][0].thanos.labels)) { 160 Object.assign(newblockPools, { [key]: blockPools[key] }); 161 break; 162 } 163 } 164 }); 165 return newblockPools; 166 };