1// Copyright (C) 2022 The Android Open Source Project 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15import {EqualsBuilder} from '../common/comparator_builder'; 16import {ColumnType} from '../common/query_result'; 17import {SortDirection} from '../common/state'; 18 19// Node in the hierarchical pivot tree. Only leaf nodes contain data from the 20// query result. 21export interface PivotTree { 22 // Whether the node should be collapsed in the UI, false by default and can 23 // be toggled with the button. 24 isCollapsed: boolean; 25 26 // Non-empty only in internal nodes. 27 children: Map<ColumnType, PivotTree>; 28 aggregates: ColumnType[]; 29 30 // Non-empty only in leaf nodes. 31 rows: ColumnType[][]; 32} 33 34export type AggregationFunction = 'COUNT'|'SUM'|'MIN'|'MAX'|'AVG'; 35 36// Queried "table column" is either: 37// 1. A real one, represented as object with table and column name. 38// 2. Pseudo-column 'count' that's rendered as '1' in SQL to use in queries like 39// `select sum(1), name from slice group by name`. 40 41export interface RegularColumn { 42 kind: 'regular'; 43 table: string; 44 column: string; 45} 46 47export interface ArgumentColumn { 48 kind: 'argument'; 49 argument: string; 50} 51 52export type TableColumn = RegularColumn|ArgumentColumn; 53 54export function tableColumnEquals(t1: TableColumn, t2: TableColumn): boolean { 55 switch (t1.kind) { 56 case 'argument': { 57 return t2.kind === 'argument' && t1.argument === t2.argument; 58 } 59 case 'regular': { 60 return t2.kind === 'regular' && t1.table === t2.table && 61 t1.column === t2.column; 62 } 63 } 64} 65 66export function toggleEnabled<T>( 67 compare: (fst: T, snd: T) => boolean, 68 arr: T[], 69 column: T, 70 enabled: boolean): void { 71 if (enabled && arr.find((value) => compare(column, value)) === undefined) { 72 arr.push(column); 73 } 74 if (!enabled) { 75 const index = arr.findIndex((value) => compare(column, value)); 76 if (index !== -1) { 77 arr.splice(index, 1); 78 } 79 } 80} 81 82export interface Aggregation { 83 aggregationFunction: AggregationFunction; 84 column: TableColumn; 85 86 // If the aggregation is sorted, the field contains a sorting direction. 87 sortDirection?: SortDirection; 88} 89 90export function aggregationEquals(agg1: Aggregation, agg2: Aggregation) { 91 return new EqualsBuilder(agg1, agg2) 92 .comparePrimitive((agg) => agg.aggregationFunction) 93 .compare(tableColumnEquals, (agg) => agg.column) 94 .equals(); 95} 96 97// Used to convert TableColumn to a string in order to store it in a Map, as 98// ES6 does not support compound Set/Map keys. This function should only be used 99// for interning keys, and does not have any requirements beyond different 100// TableColumn objects mapping to different strings. 101export function columnKey(tableColumn: TableColumn): string { 102 switch (tableColumn.kind) { 103 case 'argument': { 104 return `argument:${tableColumn.argument}`; 105 } 106 case 'regular': { 107 return `${tableColumn.table}.${tableColumn.column}`; 108 } 109 } 110} 111 112export function aggregationKey(aggregation: Aggregation): string { 113 return `${aggregation.aggregationFunction}:${columnKey(aggregation.column)}`; 114} 115