| // Copyright (C) 2026 The Android Open Source Project |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| import type {Engine} from '../../../trace_processor/engine'; |
| import { |
| NUM, |
| STR_NULL, |
| type QueryResult, |
| } from '../../../trace_processor/query_result'; |
| import { |
| ChartSource, |
| SQLChartLoader, |
| type QueryConfig, |
| type ChartLoaderResult, |
| inFilter, |
| } from './chart_sql_source'; |
| import type {ChartAggregation} from './chart_utils'; |
| import type {PieChartData, PieChartSlice} from './pie_chart'; |
| |
| /** |
| * Configuration for SQLPieChartLoader. |
| */ |
| export interface SQLPieChartLoaderOpts { |
| /** The trace processor engine to run queries against. */ |
| readonly engine: Engine; |
| |
| /** |
| * SQL query that provides the raw data. |
| * Must include both the dimension and measure columns in its output. |
| */ |
| readonly query: string; |
| |
| /** Column name for the dimension (slice labels). */ |
| readonly dimensionColumn: string; |
| |
| /** Column name for the measure (numeric values to aggregate). */ |
| readonly measureColumn: string; |
| } |
| |
| /** |
| * Per-use configuration for the pie chart loader. |
| */ |
| export interface PieChartLoaderConfig { |
| /** Aggregation function to apply to the measure column per group. */ |
| readonly aggregation: ChartAggregation; |
| |
| /** |
| * Maximum number of slices to return. Groups are sorted by aggregated |
| * value descending; the top N are kept and all remaining groups are |
| * collapsed into an "(Other)" slice. Defaults to no limit. |
| */ |
| readonly limit?: number; |
| |
| /** Filter to only include specific dimension values. */ |
| readonly filter?: ReadonlyArray<string | number>; |
| } |
| |
| /** Result returned by the pie chart loader. */ |
| export type PieChartLoaderResult = ChartLoaderResult<PieChartData>; |
| |
| /** |
| * SQL-based pie chart loader with async loading and caching. |
| * |
| * Performs grouping and aggregation directly in SQL. When a limit is |
| * set, groups beyond the top N are collapsed into an "(Other)" slice. |
| * Uses QuerySlot for caching and request deduplication. |
| */ |
| export class SQLPieChartLoader extends SQLChartLoader< |
| PieChartLoaderConfig, |
| PieChartData |
| > { |
| private readonly dimensionColumn: string; |
| private readonly measureColumn: string; |
| |
| constructor(opts: SQLPieChartLoaderOpts) { |
| super( |
| opts.engine, |
| new ChartSource({ |
| query: opts.query, |
| schema: { |
| [opts.dimensionColumn]: 'text', |
| [opts.measureColumn]: 'real', |
| }, |
| }), |
| ); |
| this.dimensionColumn = opts.dimensionColumn; |
| this.measureColumn = opts.measureColumn; |
| } |
| |
| protected buildQueryConfig(config: PieChartLoaderConfig): QueryConfig { |
| return { |
| type: 'aggregated', |
| dimensions: [{column: this.dimensionColumn}], |
| measures: [{column: this.measureColumn, aggregation: config.aggregation}], |
| filters: inFilter(this.dimensionColumn, config.filter), |
| limit: config.limit, |
| includeOther: config.limit !== undefined, |
| }; |
| } |
| |
| protected parseResult(queryResult: QueryResult): PieChartData { |
| const slices: PieChartSlice[] = []; |
| const iter = queryResult.iter({_dim: STR_NULL, _value: NUM}); |
| for (; iter.valid(); iter.next()) { |
| slices.push({label: iter._dim ?? '(null)', value: iter._value}); |
| } |
| return {slices}; |
| } |
| } |