mirror of
https://github.com/SigNoz/signoz.git
synced 2025-12-25 03:16:53 +00:00
feat: aggreagate drilldown refactor to use for tables and other panels alike
This commit is contained in:
parent
4a98c54e78
commit
a9ac3b7e15
@ -8,11 +8,8 @@ import { IBuilderQuery, Query } from 'types/api/queryBuilder/queryBuilderData';
|
||||
|
||||
import { getAggregateColumnHeader } from './drilldownUtils';
|
||||
import { AGGREGATE_OPTIONS, SUPPORTED_OPERATORS } from './menuOptions';
|
||||
import {
|
||||
getBreakoutQuery,
|
||||
getFiltersToAdd,
|
||||
getQueryData,
|
||||
} from './tableDrilldownUtils';
|
||||
import { getBreakoutQuery, getQueryData } from './tableDrilldownUtils';
|
||||
import { AggregateData } from './useAggregateDrilldown';
|
||||
|
||||
export type ContextMenuItem = ReactNode;
|
||||
|
||||
@ -45,7 +42,7 @@ export interface BreakoutOptionsProps {
|
||||
onColumnClick: (groupBy: BaseAutocompleteData) => void;
|
||||
}
|
||||
|
||||
function getGroupContextMenuConfig({
|
||||
export function getGroupContextMenuConfig({
|
||||
query,
|
||||
clickedData,
|
||||
panelType,
|
||||
@ -88,26 +85,32 @@ function getGroupContextMenuConfig({
|
||||
return {};
|
||||
}
|
||||
|
||||
function getAggregateContextMenuConfig({
|
||||
export function getAggregateContextMenuConfig({
|
||||
subMenu,
|
||||
query,
|
||||
clickedData,
|
||||
onColumnClick,
|
||||
}: Omit<ContextMenuConfigParams, 'configType'>): AggregateContextMenuConfig {
|
||||
console.log('getAggregateContextMenuConfig', { clickedData, query });
|
||||
aggregateData,
|
||||
}: {
|
||||
subMenu?: string;
|
||||
query: Query;
|
||||
onColumnClick: (key: string, query?: Query) => void;
|
||||
aggregateData: AggregateData | null;
|
||||
}): AggregateContextMenuConfig {
|
||||
console.log('getAggregateContextMenuConfig', { query, aggregateData });
|
||||
|
||||
if (subMenu === 'breakout') {
|
||||
const queryData = getQueryData(query, clickedData);
|
||||
const queryData = getQueryData(query, aggregateData);
|
||||
return {
|
||||
header: 'Breakout by',
|
||||
items: (
|
||||
<BreakoutOptions
|
||||
queryData={queryData}
|
||||
onColumnClick={(groupBy: BaseAutocompleteData): void => {
|
||||
const filtersToAdd = getFiltersToAdd(query, clickedData);
|
||||
// Use aggregateData.filters
|
||||
const filtersToAdd = aggregateData?.filters || [];
|
||||
const breakoutQuery = getBreakoutQuery(
|
||||
query,
|
||||
clickedData,
|
||||
aggregateData,
|
||||
groupBy,
|
||||
filtersToAdd,
|
||||
);
|
||||
@ -118,11 +121,16 @@ function getAggregateContextMenuConfig({
|
||||
};
|
||||
}
|
||||
|
||||
// Use aggregateData.queryName
|
||||
const queryName = aggregateData?.queryName;
|
||||
const { dataSource, aggregations } = getAggregateColumnHeader(
|
||||
query,
|
||||
clickedData?.column?.dataIndex as string,
|
||||
queryName as string,
|
||||
);
|
||||
|
||||
console.log('dataSource', dataSource);
|
||||
console.log('aggregations', aggregations);
|
||||
|
||||
return {
|
||||
header: (
|
||||
<div>
|
||||
@ -141,36 +149,3 @@ function getAggregateContextMenuConfig({
|
||||
)),
|
||||
};
|
||||
}
|
||||
|
||||
export function getContextMenuConfig({
|
||||
subMenu,
|
||||
configType,
|
||||
query,
|
||||
clickedData,
|
||||
panelType,
|
||||
onColumnClick,
|
||||
}: ContextMenuConfigParams): {
|
||||
header?: string | ReactNode;
|
||||
items?: ContextMenuItem;
|
||||
} {
|
||||
if (configType === ConfigType.GROUP) {
|
||||
return getGroupContextMenuConfig({
|
||||
query,
|
||||
clickedData,
|
||||
panelType,
|
||||
onColumnClick,
|
||||
});
|
||||
}
|
||||
|
||||
if (configType === ConfigType.AGGREGATE) {
|
||||
return getAggregateContextMenuConfig({
|
||||
subMenu,
|
||||
query,
|
||||
clickedData,
|
||||
panelType,
|
||||
onColumnClick,
|
||||
});
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
@ -122,7 +122,8 @@ export const getAggregateColumnHeader = (
|
||||
return { dataSource: '', aggregations: '' };
|
||||
}
|
||||
|
||||
const { dataSource, aggregations } = queryStep;
|
||||
console.log('queryStep', queryStep);
|
||||
const { dataSource, aggregations } = queryStep; // TODO: check if this is correct
|
||||
|
||||
// Extract aggregation expressions based on data source type
|
||||
let aggregationExpressions: string[] = [];
|
||||
|
||||
@ -13,43 +13,25 @@ import { IBuilderQuery, Query } from 'types/api/queryBuilder/queryBuilderData';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
|
||||
import { getBaseMeta } from './drilldownUtils';
|
||||
import { AggregateData } from './useAggregateDrilldown';
|
||||
/**
|
||||
* Gets the query data that matches the clicked column's dataIndex
|
||||
* Gets the query data that matches the aggregate data's queryName
|
||||
*/
|
||||
export const getQueryData = (query: Query, clickedData: any): IBuilderQuery => {
|
||||
if (!clickedData) {
|
||||
console.warn('clickedData is null in getQueryData');
|
||||
export const getQueryData = (
|
||||
query: Query,
|
||||
aggregateData: AggregateData | null,
|
||||
): IBuilderQuery => {
|
||||
if (!aggregateData) {
|
||||
console.warn('aggregateData is null in getQueryData');
|
||||
return initialQueryBuilderFormValuesMap.logs;
|
||||
}
|
||||
|
||||
const queryData = query?.builder?.queryData?.filter(
|
||||
(item: IBuilderQuery) => item.queryName === clickedData.column.dataIndex,
|
||||
(item: IBuilderQuery) => item.queryName === aggregateData.queryName,
|
||||
);
|
||||
return queryData[0];
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates filters to add to the query from columns which are part of the query.builder.queryData[].groupBy
|
||||
*/
|
||||
export const getFiltersToAdd = (
|
||||
query: Query,
|
||||
clickedData: any,
|
||||
): FilterData[] => {
|
||||
if (!clickedData) {
|
||||
console.warn('clickedData is null in getFiltersToAdd');
|
||||
return [];
|
||||
}
|
||||
|
||||
const queryData = getQueryData(query, clickedData);
|
||||
const { groupBy } = queryData;
|
||||
|
||||
return groupBy.map((item: BaseAutocompleteData) => ({
|
||||
filterKey: item.key,
|
||||
filterValue: clickedData.record[item.key],
|
||||
operator: OPERATORS['='],
|
||||
}));
|
||||
};
|
||||
|
||||
export const isEmptyFilterValue = (value: any): boolean =>
|
||||
value === '' || value === null || value === undefined || value === 'n/a';
|
||||
|
||||
@ -138,29 +120,29 @@ export const getViewQuery = (
|
||||
*/
|
||||
export const getBreakoutQuery = (
|
||||
query: Query,
|
||||
clickedData: any,
|
||||
aggregateData: AggregateData | null,
|
||||
groupBy: BaseAutocompleteData,
|
||||
filtersToAdd: FilterData[],
|
||||
): Query => {
|
||||
if (!clickedData) {
|
||||
console.warn('clickedData is null in getBreakoutQuery');
|
||||
if (!aggregateData) {
|
||||
console.warn('aggregateData is null in getBreakoutQuery');
|
||||
return query;
|
||||
}
|
||||
|
||||
console.log('>> groupBy', groupBy);
|
||||
console.log('>> clickedData', clickedData);
|
||||
console.log('>> aggregateData', aggregateData);
|
||||
console.log('>> query', query);
|
||||
|
||||
const queryWithFilters = addFilterToSelectedQuery(
|
||||
query,
|
||||
filtersToAdd,
|
||||
clickedData.column.dataIndex,
|
||||
aggregateData.queryName,
|
||||
);
|
||||
const newQuery = cloneDeep(queryWithFilters);
|
||||
|
||||
newQuery.builder.queryData = newQuery.builder.queryData.map(
|
||||
(item: IBuilderQuery) => {
|
||||
if (item.queryName === clickedData.column.dataIndex) {
|
||||
if (item.queryName === aggregateData.queryName) {
|
||||
return {
|
||||
...item,
|
||||
groupBy: [groupBy],
|
||||
|
||||
@ -1,22 +1,24 @@
|
||||
import { QueryParams } from 'constants/query';
|
||||
import ROUTES from 'constants/routes';
|
||||
import {
|
||||
getFiltersToAddToView,
|
||||
getViewQuery,
|
||||
} from 'container/QueryTable/tableDrilldownUtils';
|
||||
import { FilterData } from 'container/QueryTable/drilldownUtils';
|
||||
import { getViewQuery } from 'container/QueryTable/tableDrilldownUtils';
|
||||
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
|
||||
import { useSafeNavigate } from 'hooks/useSafeNavigate';
|
||||
import createQueryParams from 'lib/createQueryParams';
|
||||
import { ClickedData } from 'periscope/components/ContextMenu/types';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import { Query } from 'types/api/queryBuilder/queryBuilderData';
|
||||
|
||||
import {
|
||||
ConfigType,
|
||||
ContextMenuItem,
|
||||
getContextMenuConfig,
|
||||
getAggregateContextMenuConfig,
|
||||
} from './contextConfig';
|
||||
|
||||
// Type for aggregate data
|
||||
export interface AggregateData {
|
||||
queryName: string;
|
||||
filters: FilterData[];
|
||||
}
|
||||
|
||||
const getRoute = (key: string): string => {
|
||||
switch (key) {
|
||||
case 'view_logs':
|
||||
@ -33,17 +35,17 @@ const getRoute = (key: string): string => {
|
||||
const useAggregateDrilldown = ({
|
||||
query,
|
||||
widgetId,
|
||||
clickedData,
|
||||
onClose,
|
||||
subMenu,
|
||||
setSubMenu,
|
||||
aggregateData,
|
||||
}: {
|
||||
query: Query;
|
||||
widgetId: string;
|
||||
clickedData: ClickedData | null;
|
||||
onClose: () => void;
|
||||
subMenu: string;
|
||||
setSubMenu: (subMenu: string) => void;
|
||||
aggregateData: AggregateData | null;
|
||||
}): {
|
||||
aggregateDrilldownConfig: {
|
||||
header?: string | React.ReactNode;
|
||||
@ -56,7 +58,7 @@ const useAggregateDrilldown = ({
|
||||
(query: Query): void => {
|
||||
redirectWithQueryBuilderData(
|
||||
query,
|
||||
{ [QueryParams.expandedWidgetId]: widgetId },
|
||||
{ [QueryParams.expandedWidgetId]: widgetId }, // add only if view mode
|
||||
undefined,
|
||||
true,
|
||||
);
|
||||
@ -67,7 +69,7 @@ const useAggregateDrilldown = ({
|
||||
|
||||
const handleAggregateDrilldown = useCallback(
|
||||
(key: string, drilldownQuery?: Query): void => {
|
||||
console.log('Aggregate drilldown:', { clickedData, widgetId, query, key });
|
||||
console.log('Aggregate drilldown:', { widgetId, query, key, aggregateData });
|
||||
|
||||
if (key === 'breakout') {
|
||||
if (!drilldownQuery) {
|
||||
@ -80,7 +82,7 @@ const useAggregateDrilldown = ({
|
||||
}
|
||||
|
||||
const route = getRoute(key);
|
||||
const filtersToAdd = clickedData ? getFiltersToAddToView(clickedData) : [];
|
||||
const filtersToAdd = aggregateData?.filters || [];
|
||||
const viewQuery = getViewQuery(query, filtersToAdd, key);
|
||||
|
||||
let queryParams = {
|
||||
@ -105,30 +107,28 @@ const useAggregateDrilldown = ({
|
||||
onClose();
|
||||
},
|
||||
[
|
||||
clickedData,
|
||||
query,
|
||||
widgetId,
|
||||
safeNavigate,
|
||||
onClose,
|
||||
redirectToViewMode,
|
||||
setSubMenu,
|
||||
aggregateData,
|
||||
],
|
||||
);
|
||||
|
||||
const aggregateDrilldownConfig = useMemo(() => {
|
||||
if (!clickedData) {
|
||||
console.warn('clickedData is null in aggregateDrilldownConfig');
|
||||
if (!aggregateData) {
|
||||
console.warn('aggregateData is null in aggregateDrilldownConfig');
|
||||
return {};
|
||||
}
|
||||
return getContextMenuConfig({
|
||||
return getAggregateContextMenuConfig({
|
||||
subMenu,
|
||||
configType: ConfigType.AGGREGATE,
|
||||
query,
|
||||
clickedData,
|
||||
panelType: 'table',
|
||||
onColumnClick: handleAggregateDrilldown,
|
||||
aggregateData,
|
||||
});
|
||||
}, [handleAggregateDrilldown, clickedData, query, subMenu]);
|
||||
}, [handleAggregateDrilldown, query, subMenu, aggregateData]);
|
||||
return { aggregateDrilldownConfig };
|
||||
};
|
||||
|
||||
|
||||
@ -2,9 +2,11 @@ import { QueryParams } from 'constants/query';
|
||||
import { addFilterToQuery } from 'container/QueryTable/drilldownUtils';
|
||||
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
|
||||
import { ClickedData } from 'periscope/components/ContextMenu/types';
|
||||
import { useCallback } from 'react';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import { Query } from 'types/api/queryBuilder/queryBuilderData';
|
||||
|
||||
import { getGroupContextMenuConfig } from './contextConfig';
|
||||
|
||||
const useFilterDrilldown = ({
|
||||
query,
|
||||
widgetId,
|
||||
@ -15,7 +17,12 @@ const useFilterDrilldown = ({
|
||||
widgetId: string;
|
||||
clickedData: ClickedData | null;
|
||||
onClose: () => void;
|
||||
}): { handleFilterDrilldown: (operator: string) => void } => {
|
||||
}): {
|
||||
filterDrilldownConfig: {
|
||||
header?: string | React.ReactNode;
|
||||
items?: React.ReactNode;
|
||||
};
|
||||
} => {
|
||||
const { redirectWithQueryBuilderData } = useQueryBuilder();
|
||||
|
||||
const redirectToViewMode = useCallback(
|
||||
@ -47,8 +54,21 @@ const useFilterDrilldown = ({
|
||||
[onClose, clickedData, query, redirectToViewMode],
|
||||
);
|
||||
|
||||
const filterDrilldownConfig = useMemo(() => {
|
||||
if (!clickedData) {
|
||||
console.warn('clickedData is null in filterDrilldownConfig');
|
||||
return {};
|
||||
}
|
||||
return getGroupContextMenuConfig({
|
||||
query,
|
||||
clickedData,
|
||||
panelType: 'table',
|
||||
onColumnClick: handleFilterDrilldown,
|
||||
});
|
||||
}, [handleFilterDrilldown, clickedData, query]);
|
||||
|
||||
return {
|
||||
handleFilterDrilldown,
|
||||
filterDrilldownConfig,
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@ -1,7 +1,5 @@
|
||||
import {
|
||||
ConfigType,
|
||||
getContextMenuConfig,
|
||||
} from 'container/QueryTable/contextConfig';
|
||||
import { ConfigType } from 'container/QueryTable/contextConfig';
|
||||
import { getFiltersToAddToView } from 'container/QueryTable/tableDrilldownUtils';
|
||||
import useAggregateDrilldown from 'container/QueryTable/useAggregateDrilldown';
|
||||
import useFilterDrilldown from 'container/QueryTable/useFilterDrilldown';
|
||||
import { useGetCompositeQueryParam } from 'hooks/queryBuilder/useGetCompositeQueryParam';
|
||||
@ -34,34 +32,29 @@ export function useTableContextMenu({
|
||||
};
|
||||
} {
|
||||
const drilldownQuery = useGetCompositeQueryParam() || query;
|
||||
const { handleFilterDrilldown } = useFilterDrilldown({
|
||||
const { filterDrilldownConfig } = useFilterDrilldown({
|
||||
query: drilldownQuery,
|
||||
widgetId,
|
||||
clickedData,
|
||||
onClose,
|
||||
});
|
||||
|
||||
const filterDrilldownConfig = useMemo(() => {
|
||||
if (!clickedData) {
|
||||
console.warn('clickedData is null in filterDrilldownConfig');
|
||||
return {};
|
||||
}
|
||||
return getContextMenuConfig({
|
||||
configType: ConfigType.GROUP,
|
||||
query,
|
||||
clickedData,
|
||||
panelType: 'table',
|
||||
onColumnClick: handleFilterDrilldown,
|
||||
});
|
||||
}, [handleFilterDrilldown, clickedData, query]);
|
||||
const aggregateData = useMemo(() => {
|
||||
if (!clickedData?.column?.isValueColumn) return null;
|
||||
|
||||
return {
|
||||
queryName: String(clickedData.column.dataIndex || ''),
|
||||
filters: getFiltersToAddToView(clickedData) || [],
|
||||
};
|
||||
}, [clickedData]);
|
||||
|
||||
const { aggregateDrilldownConfig } = useAggregateDrilldown({
|
||||
query: drilldownQuery,
|
||||
widgetId,
|
||||
clickedData,
|
||||
onClose,
|
||||
subMenu,
|
||||
setSubMenu,
|
||||
aggregateData,
|
||||
});
|
||||
|
||||
const menuItemsConfig = useMemo(() => {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user