diff --git a/packages/grafana-schema/src/raw/composable/azuremonitor/dataquery/x/AzureMonitorDataQuery_types.gen.ts b/packages/grafana-schema/src/raw/composable/azuremonitor/dataquery/x/AzureMonitorDataQuery_types.gen.ts
index 55ad5313063..d889801bfb4 100644
--- a/packages/grafana-schema/src/raw/composable/azuremonitor/dataquery/x/AzureMonitorDataQuery_types.gen.ts
+++ b/packages/grafana-schema/src/raw/composable/azuremonitor/dataquery/x/AzureMonitorDataQuery_types.gen.ts
@@ -329,8 +329,17 @@ export enum BuilderQueryEditorOrderByOptions {
Desc = 'desc',
}
+export enum BuilderQueryEditorReduceParameterTypes {
+ Generic = 'generic',
+ Numeric = 'numeric',
+}
+
export interface BuilderQueryEditorProperty {
name: string;
+ /**
+ * Optional parameter type for function properties
+ */
+ parameterType?: BuilderQueryEditorReduceParameterTypes;
type: BuilderQueryEditorPropertyType;
}
diff --git a/pkg/tsdb/azuremonitor/kinds/dataquery/types_dataquery_gen.go b/pkg/tsdb/azuremonitor/kinds/dataquery/types_dataquery_gen.go
index e1deb1f008b..47594fbd55c 100644
--- a/pkg/tsdb/azuremonitor/kinds/dataquery/types_dataquery_gen.go
+++ b/pkg/tsdb/azuremonitor/kinds/dataquery/types_dataquery_gen.go
@@ -223,6 +223,8 @@ func NewBuilderQueryEditorPropertyExpression() *BuilderQueryEditorPropertyExpres
type BuilderQueryEditorProperty struct {
Type BuilderQueryEditorPropertyType `json:"type"`
Name string `json:"name"`
+ // Optional parameter type for function properties
+ ParameterType *BuilderQueryEditorReduceParameterTypes `json:"parameterType,omitempty"`
}
// NewBuilderQueryEditorProperty creates a new BuilderQueryEditorProperty object.
@@ -242,6 +244,13 @@ const (
BuilderQueryEditorPropertyTypeInterval BuilderQueryEditorPropertyType = "interval"
)
+type BuilderQueryEditorReduceParameterTypes string
+
+const (
+ BuilderQueryEditorReduceParameterTypesGeneric BuilderQueryEditorReduceParameterTypes = "generic"
+ BuilderQueryEditorReduceParameterTypesNumeric BuilderQueryEditorReduceParameterTypes = "numeric"
+)
+
type BuilderQueryEditorExpressionType string
const (
diff --git a/public/app/plugins/datasource/azuremonitor/components/LogsQueryBuilder/AggregateItem.test.tsx b/public/app/plugins/datasource/azuremonitor/components/LogsQueryBuilder/AggregateItem.test.tsx
new file mode 100644
index 00000000000..3c472fcb69e
--- /dev/null
+++ b/public/app/plugins/datasource/azuremonitor/components/LogsQueryBuilder/AggregateItem.test.tsx
@@ -0,0 +1,191 @@
+import { render, screen } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
+
+import {
+ BuilderQueryEditorExpressionType,
+ BuilderQueryEditorPropertyType,
+ BuilderQueryEditorReduceExpression,
+ BuilderQueryEditorReduceParameterTypes,
+} from '../../dataquery.gen';
+
+import AggregateItem from './AggregateItem';
+
+describe('AggregateItem', () => {
+ const mockColumns = [
+ { label: 'TimeGenerated', value: 'TimeGenerated' },
+ { label: 'Level', value: 'Level' },
+ { label: 'Message', value: 'Message' },
+ ];
+
+ const mockTemplateVariables = { label: '$variable', value: '$variable' };
+
+ const defaultAggregate: BuilderQueryEditorReduceExpression = {
+ reduce: {
+ name: 'sum',
+ type: BuilderQueryEditorPropertyType.Function,
+ parameterType: BuilderQueryEditorReduceParameterTypes.Numeric,
+ },
+ property: {
+ name: 'TimeGenerated',
+ type: BuilderQueryEditorPropertyType.String,
+ },
+ };
+
+ const defaultProps = {
+ aggregate: defaultAggregate,
+ columns: mockColumns,
+ onChange: jest.fn(),
+ onDelete: jest.fn(),
+ templateVariableOptions: mockTemplateVariables,
+ };
+
+ beforeEach(() => {
+ jest.clearAllMocks();
+ });
+
+ it('renders aggregate function select (with column field)', () => {
+ render();
+ expect(screen.getByLabelText('Aggregate function')).toBeInTheDocument();
+ expect(screen.getByLabelText('Column')).toBeInTheDocument();
+ });
+
+ it('does not render column select for count aggregates', () => {
+ const countAggregate = {
+ ...defaultAggregate,
+ reduce: {
+ name: 'count',
+ type: BuilderQueryEditorPropertyType.Function,
+ parameterType: BuilderQueryEditorReduceParameterTypes.Generic,
+ },
+ };
+ render();
+ expect(screen.queryByLabelText('Column')).not.toBeInTheDocument();
+ });
+
+ it('renders percentile input and OF label for percentile aggregate', () => {
+ const percentileAggregate: BuilderQueryEditorReduceExpression = {
+ reduce: {
+ name: 'percentile',
+ type: BuilderQueryEditorPropertyType.Function,
+ parameterType: BuilderQueryEditorReduceParameterTypes.Numeric,
+ },
+ parameters: [
+ {
+ type: BuilderQueryEditorExpressionType.Function_parameter,
+ fieldType: BuilderQueryEditorPropertyType.Number,
+ value: '95',
+ },
+ {
+ type: BuilderQueryEditorExpressionType.Function_parameter,
+ fieldType: BuilderQueryEditorPropertyType.String,
+ value: 'TimeGenerated',
+ },
+ ],
+ property: {
+ name: 'TimeGenerated',
+ type: BuilderQueryEditorPropertyType.String,
+ },
+ };
+ render();
+ expect(screen.getByDisplayValue('95')).toBeInTheDocument();
+ expect(screen.getByText('OF')).toBeInTheDocument();
+ });
+
+ it('calls onChange when aggregate function changes', async () => {
+ render();
+
+ const select = screen.getByLabelText('Aggregate function');
+ await userEvent.click(select);
+
+ const avgOption = await screen.findByText('avg');
+ await userEvent.click(avgOption);
+
+ expect(defaultProps.onChange).toHaveBeenCalledWith(
+ expect.objectContaining({
+ reduce: expect.objectContaining({
+ name: 'avg',
+ parameterType: BuilderQueryEditorReduceParameterTypes.Numeric,
+ }),
+ })
+ );
+ });
+
+ it('calls onChange when column changes', async () => {
+ render();
+
+ const columnSelect = screen.getByLabelText('Column');
+ await userEvent.click(columnSelect);
+ const levelOption = await screen.findByText('Level');
+ await userEvent.click(levelOption);
+
+ expect(defaultProps.onChange).toHaveBeenCalledWith(
+ expect.objectContaining({
+ property: expect.objectContaining({
+ name: 'Level',
+ }),
+ })
+ );
+ });
+
+ it('calls onChange when percentile value changes', async () => {
+ const percentileAggregate: BuilderQueryEditorReduceExpression = {
+ reduce: {
+ name: 'percentile',
+ type: BuilderQueryEditorPropertyType.Function,
+ parameterType: BuilderQueryEditorReduceParameterTypes.Numeric,
+ },
+ parameters: [
+ {
+ type: BuilderQueryEditorExpressionType.Function_parameter,
+ fieldType: BuilderQueryEditorPropertyType.Number,
+ value: '95',
+ },
+ {
+ type: BuilderQueryEditorExpressionType.Function_parameter,
+ fieldType: BuilderQueryEditorPropertyType.String,
+ value: 'TimeGenerated',
+ },
+ ],
+ };
+ render();
+
+ const percentileInput = screen.getByDisplayValue('95');
+ await userEvent.clear(percentileInput);
+ await userEvent.type(percentileInput, '99');
+
+ expect(defaultProps.onChange).toHaveBeenCalledWith(
+ expect.objectContaining({
+ parameters: expect.arrayContaining([expect.objectContaining({ value: '99' })]),
+ })
+ );
+ });
+
+ it('calls onDelete when delete button clicked', async () => {
+ render();
+
+ const deleteButton = screen.getByLabelText('Remove');
+ await userEvent.click(deleteButton);
+ expect(defaultProps.onDelete).toHaveBeenCalledTimes(1);
+ });
+
+ it('includes template variables in column options', async () => {
+ render();
+
+ const columnSelect = screen.getByLabelText('Column');
+ await userEvent.click(columnSelect);
+ expect(await screen.findByText('$variable')).toBeInTheDocument();
+ });
+
+ it('handles array of template variables', async () => {
+ const arrayTemplateVars = [
+ { label: '$var1', value: '$var1' },
+ { label: '$var2', value: '$var2' },
+ ];
+ render();
+
+ const columnSelect = screen.getByLabelText('Column');
+ await userEvent.click(columnSelect);
+
+ expect(await screen.findByText('$var1')).toBeInTheDocument();
+ });
+});
diff --git a/public/app/plugins/datasource/azuremonitor/components/LogsQueryBuilder/AggregateItem.tsx b/public/app/plugins/datasource/azuremonitor/components/LogsQueryBuilder/AggregateItem.tsx
index 867098f7574..2eacf01b8bd 100644
--- a/public/app/plugins/datasource/azuremonitor/components/LogsQueryBuilder/AggregateItem.tsx
+++ b/public/app/plugins/datasource/azuremonitor/components/LogsQueryBuilder/AggregateItem.tsx
@@ -9,6 +9,7 @@ import {
BuilderQueryEditorExpressionType,
BuilderQueryEditorPropertyType,
BuilderQueryEditorReduceExpression,
+ BuilderQueryEditorReduceParameterTypes,
} from '../../dataquery.gen';
import { aggregateOptions, inputFieldSize } from './utils';
@@ -65,8 +66,16 @@ const AggregateItem: React.FC = ({
};
const handleAggregateChange = (funcName?: string) => {
+ const functionParameterType =
+ aggregateOptions.find((option) => option.value === (funcName || ''))?.parameterType ||
+ BuilderQueryEditorReduceParameterTypes.Generic;
+
updateAggregate({
- reduce: { name: funcName || '', type: BuilderQueryEditorPropertyType.Function },
+ reduce: {
+ name: funcName || '',
+ type: BuilderQueryEditorPropertyType.Function,
+ parameterType: functionParameterType,
+ },
});
};
diff --git a/public/app/plugins/datasource/azuremonitor/components/LogsQueryBuilder/AggregationSection.test.tsx b/public/app/plugins/datasource/azuremonitor/components/LogsQueryBuilder/AggregationSection.test.tsx
new file mode 100644
index 00000000000..ea073e1e8e5
--- /dev/null
+++ b/public/app/plugins/datasource/azuremonitor/components/LogsQueryBuilder/AggregationSection.test.tsx
@@ -0,0 +1,293 @@
+import { render, screen } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
+
+import {
+ AzureQueryType,
+ BuilderQueryEditorExpressionType,
+ BuilderQueryEditorPropertyType,
+ BuilderQueryEditorReduceExpression,
+ BuilderQueryEditorReduceParameterTypes,
+} from '../../dataquery.gen';
+import { AzureMonitorQuery } from '../../types/query';
+
+import { AggregateSection } from './AggregationSection';
+
+describe('AggregationSection', () => {
+ const mockAllColumns = [
+ { name: 'TimeGenerated', type: 'datetime' },
+ { name: 'Level', type: 'string' },
+ { name: 'Count', type: 'int' },
+ { name: 'Duration', type: 'real' },
+ ];
+
+ const mockTemplateVariables = { label: '$variable', value: '$variable' };
+
+ const createMockQuery = (reduce?: BuilderQueryEditorReduceExpression[]): AzureMonitorQuery => ({
+ refId: 'A',
+ queryType: AzureQueryType.LogAnalytics,
+ azureLogAnalytics: {
+ builderQuery: {
+ from: {
+ type: BuilderQueryEditorExpressionType.Property,
+ property: { type: BuilderQueryEditorPropertyType.String, name: 'AppRequests' },
+ },
+ columns: {
+ type: BuilderQueryEditorExpressionType.Property,
+ columns: [],
+ },
+ reduce: {
+ type: BuilderQueryEditorExpressionType.Reduce,
+ expressions: reduce || [],
+ },
+ where: {
+ type: BuilderQueryEditorExpressionType.And,
+ expressions: [],
+ },
+ groupBy: {
+ type: BuilderQueryEditorExpressionType.Group_by,
+ expressions: [],
+ },
+ },
+ },
+ });
+
+ const defaultProps = {
+ query: createMockQuery(),
+ allColumns: mockAllColumns,
+ templateVariableOptions: mockTemplateVariables,
+ buildAndUpdateQuery: jest.fn(),
+ };
+
+ beforeEach(() => {
+ jest.clearAllMocks();
+ });
+
+ it('renders the aggregate section', () => {
+ render();
+ expect(screen.getByTestId('aggregate-section')).toBeInTheDocument();
+ });
+
+ it('renders empty list when no aggregates exist', () => {
+ render();
+ const addButton = screen.getByRole('button', { name: /add/i });
+ expect(addButton).toBeInTheDocument();
+ });
+
+ it('renders existing aggregates', () => {
+ const existingAggregates: BuilderQueryEditorReduceExpression[] = [
+ {
+ reduce: {
+ name: 'sum',
+ type: BuilderQueryEditorPropertyType.Function,
+ parameterType: BuilderQueryEditorReduceParameterTypes.Numeric,
+ },
+ property: {
+ name: 'Count',
+ type: BuilderQueryEditorPropertyType.String,
+ },
+ },
+ {
+ reduce: {
+ name: 'avg',
+ type: BuilderQueryEditorPropertyType.Function,
+ parameterType: BuilderQueryEditorReduceParameterTypes.Numeric,
+ },
+ property: {
+ name: 'Duration',
+ type: BuilderQueryEditorPropertyType.String,
+ },
+ },
+ ];
+
+ const queryWithAggregates = createMockQuery(existingAggregates);
+ render();
+
+ expect(screen.getAllByLabelText('Aggregate function')).toHaveLength(2);
+ });
+
+ it('calls buildAndUpdateQuery when aggregate is added', async () => {
+ render();
+
+ const addButton = screen.getByRole('button', { name: /add/i });
+ await userEvent.click(addButton);
+
+ expect(defaultProps.buildAndUpdateQuery).toHaveBeenCalledWith({
+ reduce: expect.arrayContaining([expect.objectContaining({})]),
+ });
+ });
+
+ it('calls buildAndUpdateQuery when aggregate is deleted', async () => {
+ const existingAggregates: BuilderQueryEditorReduceExpression[] = [
+ {
+ reduce: {
+ name: 'sum',
+ type: BuilderQueryEditorPropertyType.Function,
+ parameterType: BuilderQueryEditorReduceParameterTypes.Numeric,
+ },
+ property: {
+ name: 'Count',
+ type: BuilderQueryEditorPropertyType.String,
+ },
+ },
+ ];
+ const avgAggregate = {
+ reduce: {
+ name: 'avg',
+ type: BuilderQueryEditorPropertyType.Function,
+ parameterType: BuilderQueryEditorReduceParameterTypes.Numeric,
+ },
+ property: {
+ name: 'Duration',
+ type: BuilderQueryEditorPropertyType.String,
+ },
+ };
+ existingAggregates.push(avgAggregate);
+
+ const queryWithAggregates = createMockQuery(existingAggregates);
+ render();
+
+ const deleteButton = (await screen.findAllByLabelText('Remove'))[0];
+ await userEvent.click(deleteButton);
+
+ expect(defaultProps.buildAndUpdateQuery).toHaveBeenCalledWith({
+ reduce: [avgAggregate],
+ });
+ expect(screen.getAllByLabelText('Aggregate function')).toHaveLength(1);
+ });
+
+ it('provides numeric columns for numeric aggregate functions', async () => {
+ const numericAggregate: BuilderQueryEditorReduceExpression[] = [
+ {
+ reduce: {
+ name: 'sum',
+ type: BuilderQueryEditorPropertyType.Function,
+ parameterType: BuilderQueryEditorReduceParameterTypes.Numeric,
+ },
+ },
+ ];
+
+ const queryWithAggregate = createMockQuery(numericAggregate);
+ render();
+
+ const columnSelect = screen.getByLabelText('Column');
+ await userEvent.click(columnSelect);
+
+ expect(await screen.getByText('Count')).toBeInTheDocument();
+ expect(await screen.getByText('Duration')).toBeInTheDocument();
+
+ expect(screen.queryByText('Level')).not.toBeInTheDocument();
+ });
+
+ it('provides all columns for generic aggregate functions', async () => {
+ const genericAggregate: BuilderQueryEditorReduceExpression[] = [
+ {
+ reduce: {
+ name: 'min',
+ type: BuilderQueryEditorPropertyType.Function,
+ parameterType: BuilderQueryEditorReduceParameterTypes.Generic,
+ },
+ },
+ ];
+
+ const queryWithAggregate = createMockQuery(genericAggregate);
+ render();
+
+ const columnSelect = screen.getByLabelText('Column');
+ await userEvent.click(columnSelect);
+
+ expect(await screen.getByText('TimeGenerated')).toBeInTheDocument();
+ expect(await screen.getByText('Level')).toBeInTheDocument();
+ expect(await screen.getByText('Count')).toBeInTheDocument();
+ expect(await screen.getByText('Duration')).toBeInTheDocument();
+ });
+
+ it('resets aggregates when table changes', () => {
+ const existingAggregates: BuilderQueryEditorReduceExpression[] = [
+ {
+ reduce: {
+ name: 'sum',
+ type: BuilderQueryEditorPropertyType.Function,
+ parameterType: BuilderQueryEditorReduceParameterTypes.Numeric,
+ },
+ property: {
+ name: 'Count',
+ type: BuilderQueryEditorPropertyType.String,
+ },
+ },
+ ];
+
+ const queryWithAggregates = createMockQuery(existingAggregates);
+ const { rerender } = render();
+
+ const newQuery = createMockQuery(existingAggregates);
+ newQuery.azureLogAnalytics!.builderQuery!.from!.property.name = 'AppEvents';
+
+ rerender();
+
+ const addButton = screen.getByRole('button', { name: /add/i });
+ expect(addButton).toBeInTheDocument();
+ });
+
+ it('uses selected columns when available', async () => {
+ const aggregate: BuilderQueryEditorReduceExpression[] = [
+ {
+ reduce: {
+ name: 'sum',
+ type: BuilderQueryEditorPropertyType.Function,
+ parameterType: BuilderQueryEditorReduceParameterTypes.Generic,
+ },
+ },
+ ];
+
+ const query = createMockQuery(aggregate);
+ query.azureLogAnalytics!.builderQuery = {
+ ...query.azureLogAnalytics!.builderQuery,
+ columns: {
+ columns: ['TimeGenerated', 'Level'],
+ type: BuilderQueryEditorExpressionType.Property,
+ },
+ };
+
+ render();
+
+ const columnSelect = screen.getByLabelText('Column');
+ await userEvent.click(columnSelect);
+
+ expect(await screen.getByText('TimeGenerated')).toBeInTheDocument();
+ expect(await screen.getByText('Level')).toBeInTheDocument();
+
+ expect(screen.queryByText('Count')).not.toBeInTheDocument();
+ expect(screen.queryByText('Duration')).not.toBeInTheDocument();
+ });
+
+ it('falls back to all columns when no columns selected', async () => {
+ const aggregate: BuilderQueryEditorReduceExpression[] = [
+ {
+ reduce: {
+ name: 'sum',
+ type: BuilderQueryEditorPropertyType.Function,
+ parameterType: BuilderQueryEditorReduceParameterTypes.Generic,
+ },
+ },
+ ];
+
+ const query = createMockQuery(aggregate);
+ query.azureLogAnalytics!.builderQuery = {
+ ...query.azureLogAnalytics!.builderQuery,
+ columns: {
+ columns: [],
+ type: BuilderQueryEditorExpressionType.Property,
+ },
+ };
+
+ render();
+
+ const columnSelect = screen.getByLabelText('Column');
+ await userEvent.click(columnSelect);
+
+ expect(await screen.getByText('TimeGenerated')).toBeInTheDocument();
+ expect(await screen.getByText('Level')).toBeInTheDocument();
+ expect(await screen.getByText('Count')).toBeInTheDocument();
+ expect(await screen.getByText('Duration')).toBeInTheDocument();
+ });
+});
diff --git a/public/app/plugins/datasource/azuremonitor/components/LogsQueryBuilder/AggregationSection.tsx b/public/app/plugins/datasource/azuremonitor/components/LogsQueryBuilder/AggregationSection.tsx
index 4f3a1050a14..6910792685d 100644
--- a/public/app/plugins/datasource/azuremonitor/components/LogsQueryBuilder/AggregationSection.tsx
+++ b/public/app/plugins/datasource/azuremonitor/components/LogsQueryBuilder/AggregationSection.tsx
@@ -4,12 +4,12 @@ import { SelectableValue } from '@grafana/data';
import { t } from '@grafana/i18n';
import { EditorField, EditorFieldGroup, EditorList, EditorRow } from '@grafana/plugin-ui';
-import { BuilderQueryEditorReduceExpression } from '../../dataquery.gen';
+import { BuilderQueryEditorReduceExpression, BuilderQueryEditorReduceParameterTypes } from '../../dataquery.gen';
import { AzureLogAnalyticsMetadataColumn } from '../../types/logAnalyticsMetadata';
import { AzureMonitorQuery } from '../../types/query';
import AggregateItem from './AggregateItem';
-import { BuildAndUpdateOptions } from './utils';
+import { BuildAndUpdateOptions, isNumericColumn } from './utils';
interface AggregateSectionProps {
query: AzureMonitorQuery;
@@ -43,6 +43,10 @@ export const AggregateSection: React.FC = ({
const availableColumns: Array> = builderQuery?.columns?.columns?.length
? builderQuery.columns.columns.map((col) => ({ label: col, value: col }))
: allColumns.map((col) => ({ label: col.name, value: col.name }));
+ const numericColumns: Array> = allColumns.filter(isNumericColumn).map((col) => ({
+ label: col.name,
+ value: col.name,
+ }));
const onChange = (newItems: Array>) => {
setAggregates(newItems);
@@ -82,7 +86,12 @@ export const AggregateSection: React.FC = ({
@@ -93,6 +102,7 @@ export const AggregateSection: React.FC = ({
function makeRenderAggregate(
availableColumns: Array>,
+ numericColumns: Array>,
onDeleteAggregate: (aggregate: BuilderQueryEditorReduceExpression) => void,
templateVariableOptions: SelectableValue
) {
@@ -105,7 +115,11 @@ function makeRenderAggregate(
aggregate={item}
onChange={onChange}
onDelete={() => onDeleteAggregate(item)}
- columns={availableColumns}
+ columns={
+ item.reduce?.name && item.reduce.parameterType === BuilderQueryEditorReduceParameterTypes.Numeric
+ ? numericColumns
+ : availableColumns
+ }
templateVariableOptions={templateVariableOptions}
/>
);
diff --git a/public/app/plugins/datasource/azuremonitor/components/LogsQueryBuilder/utils.ts b/public/app/plugins/datasource/azuremonitor/components/LogsQueryBuilder/utils.ts
index 062dac286f9..cb660fb98e9 100644
--- a/public/app/plugins/datasource/azuremonitor/components/LogsQueryBuilder/utils.ts
+++ b/public/app/plugins/datasource/azuremonitor/components/LogsQueryBuilder/utils.ts
@@ -9,6 +9,7 @@ import {
BuilderQueryEditorPropertyExpression,
BuilderQueryEditorPropertyType,
BuilderQueryEditorReduceExpression,
+ BuilderQueryEditorReduceParameterTypes,
BuilderQueryEditorWhereExpression,
BuilderQueryExpression,
} from '../../dataquery.gen';
@@ -93,12 +94,17 @@ export interface BuildAndUpdateOptions {
}
export const aggregateOptions = [
- { label: 'sum', value: 'sum' },
- { label: 'avg', value: 'avg' },
- { label: 'percentile', value: 'percentile' },
- { label: 'count', value: 'count' },
- { label: 'min', value: 'min' },
- { label: 'max', value: 'max' },
- { label: 'dcount', value: 'dcount' },
- { label: 'stdev', value: 'stdev' },
+ { label: 'sum', value: 'sum', parameterType: BuilderQueryEditorReduceParameterTypes.Numeric },
+ { label: 'avg', value: 'avg', parameterType: BuilderQueryEditorReduceParameterTypes.Numeric },
+ { label: 'percentile', value: 'percentile', parameterType: BuilderQueryEditorReduceParameterTypes.Numeric },
+ { label: 'stdev', value: 'stdev', parameterType: BuilderQueryEditorReduceParameterTypes.Numeric },
+ { label: 'min', value: 'min', parameterType: BuilderQueryEditorReduceParameterTypes.Generic },
+ { label: 'max', value: 'max', parameterType: BuilderQueryEditorReduceParameterTypes.Generic },
+ { label: 'count', value: 'count', parameterType: BuilderQueryEditorReduceParameterTypes.Generic },
+ { label: 'dcount', value: 'dcount', parameterType: BuilderQueryEditorReduceParameterTypes.Generic },
];
+
+export const isNumericColumn = (column: AzureLogAnalyticsMetadataColumn): boolean => {
+ const numericTypes = ['decimal', 'int', 'long', 'real'];
+ return numericTypes.includes(column.type);
+};
diff --git a/public/app/plugins/datasource/azuremonitor/dataquery.cue b/public/app/plugins/datasource/azuremonitor/dataquery.cue
index 4fd52d5f297..403fd1ea72e 100644
--- a/public/app/plugins/datasource/azuremonitor/dataquery.cue
+++ b/public/app/plugins/datasource/azuremonitor/dataquery.cue
@@ -175,10 +175,13 @@ composableKinds: DataQuery: {
#BuilderQueryEditorExpressionType: "property" | "operator" | "reduce" | "function_parameter" | "group_by" | "or" | "and" | "order_by" @cuetsy(kind="enum", memberNames:"Property|Operator|Reduce|FunctionParameter|GroupBy|Or|And|OrderBy")
#BuilderQueryEditorPropertyType: "number" | "string" | "boolean" | "datetime" | "time_span" | "function" | "interval" @cuetsy(kind="enum", memberNames:"Number|String|Boolean|Datetime|TimeSpan|Function|Interval")
#BuilderQueryEditorOrderByOptions: "asc" | "desc" @cuetsy(kind="enum", memberNames:"Asc|Desc")
+ #BuilderQueryEditorReduceParameterTypes: "generic" | "numeric" @cuetsy(kind="enum", memberNames:"Asc|Desc")
#BuilderQueryEditorProperty: {
type: #BuilderQueryEditorPropertyType
name: string
+ // Optional parameter type for function properties
+ parameterType?: #BuilderQueryEditorReduceParameterTypes
} @cuetsy(kind="interface")
#BuilderQueryEditorPropertyExpression: {
diff --git a/public/app/plugins/datasource/azuremonitor/dataquery.gen.ts b/public/app/plugins/datasource/azuremonitor/dataquery.gen.ts
index c4b986970ca..2ed7b87a5ce 100644
--- a/public/app/plugins/datasource/azuremonitor/dataquery.gen.ts
+++ b/public/app/plugins/datasource/azuremonitor/dataquery.gen.ts
@@ -327,8 +327,17 @@ export enum BuilderQueryEditorOrderByOptions {
Desc = 'desc',
}
+export enum BuilderQueryEditorReduceParameterTypes {
+ Generic = 'generic',
+ Numeric = 'numeric',
+}
+
export interface BuilderQueryEditorProperty {
name: string;
+ /**
+ * Optional parameter type for function properties
+ */
+ parameterType?: BuilderQueryEditorReduceParameterTypes;
type: BuilderQueryEditorPropertyType;
}