Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature 表格和透视表支持自定义排序 #2287

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
import lombok.Data;
import lombok.EqualsAndHashCode;

import java.util.List;

@Data
@EqualsAndHashCode(callSuper = true)
public class OrderOperator extends ColumnOperator {
Expand All @@ -29,9 +31,12 @@ public class OrderOperator extends ColumnOperator {

private SqlOperator operator;

private List<String> value;

public enum SqlOperator {
ASC,
DESC
DESC,
CUSTOMIZE
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,16 @@
import datart.core.data.provider.SingleTypedValue;
import datart.core.data.provider.sql.*;
import datart.data.provider.calcite.custom.CustomSqlBetweenOperator;
import org.apache.calcite.avatica.util.Casing;
import org.apache.calcite.avatica.util.Quoting;
import org.apache.calcite.sql.*;
import org.apache.calcite.sql.fun.SqlBetweenOperator;
import org.apache.calcite.sql.fun.SqlStdOperatorTable;
import org.apache.calcite.sql.parser.SqlParseException;
import org.apache.calcite.sql.parser.SqlParser;
import org.apache.calcite.sql.parser.SqlParserPos;
import org.apache.calcite.sql.parser.impl.SqlParserImpl;
import org.apache.calcite.sql.validate.SqlConformanceEnum;
import org.apache.commons.lang3.StringUtils;
import org.springframework.util.CollectionUtils;

Expand Down Expand Up @@ -268,6 +273,30 @@ private SqlNode createOrderNode(OrderOperator operator) {
if (operator.getOperator() == OrderOperator.SqlOperator.DESC) {
return new SqlBasicCall(SqlStdOperatorTable.DESC,
new SqlNode[]{sqlNode}, SqlParserPos.ZERO);
} else if (operator.getOperator() == OrderOperator.SqlOperator.CUSTOMIZE) {
// implements customize sort of single none-num field by 'case + when'
List<String> values = operator.getValue();
StringBuffer sortExpr = new StringBuffer();
sortExpr.append("case ");
for (int i = 0; i < values.size(); i++) {
String v = values.get(i);
sortExpr.append("when " + operator.getColumnKey() + " = '" + v + "' then " + i);
}
sortExpr.append(" end");

SqlParser.Config config = SqlParser.config()
.withParserFactory(SqlParserImpl.FACTORY)
.withQuotedCasing(Casing.UNCHANGED)
.withUnquotedCasing(Casing.UNCHANGED)
.withConformance(SqlConformanceEnum.LENIENT)
.withCaseSensitive(true)
.withQuoting(Quoting.BRACKET);
try {
SqlParser parser = SqlParser.create(sortExpr.toString(), config);
return parser.parseExpression();
} catch (SqlParseException e) {
throw new RuntimeException(e);
}
} else {
return sqlNode;
}
Expand Down
9 changes: 6 additions & 3 deletions frontend/src/app/components/ChartEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ import {
} from 'app/utils/ChartEventListenerHelper';
import {
clearRuntimeDateLevelFieldsInChartConfig,
clearUnsupportedChartConfig,
filterCurrentUsedComputedFields,
getValue,
} from 'app/utils/chartHelper';
Expand Down Expand Up @@ -315,8 +316,10 @@ export const ChartEditor: FC<ChartEditorProps> = ({
setChart(c);

const targetChartConfig = CloneValueDeep(c.config);
const finalChartConfig = clearRuntimeDateLevelFieldsInChartConfig(
transferChartConfigs(targetChartConfig, shadowChartConfig || chartConfig),
const finalChartConfig = clearUnsupportedChartConfig(
clearRuntimeDateLevelFieldsInChartConfig(
transferChartConfigs(targetChartConfig, shadowChartConfig || chartConfig)
)
);

const computedFields = updateBy(dataview?.computedFields || [], draft => {
Expand Down Expand Up @@ -591,7 +594,7 @@ export const ChartEditor: FC<ChartEditorProps> = ({
{
...builder.build(),
...{
analytics: dataChartId ? false : true,
analytics: !dataChartId,
vizName: backendChart?.name || 'chart',
vizId: isWidget ? widgetId : dataChartId,
vizType: isWidget ? 'widget' : 'dataChart',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ const config: ChartConfig = {
key: 'mixed',
required: true,
type: 'mixed',
allowFieldCustomizeSort: true,
},
{
label: 'filter',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -588,16 +588,16 @@ class PivotSheetChart extends ReactChart {
): Array<SortParam> {
return sectionConfigRows
.map(config => {
if (!config?.sort?.type || config?.sort?.type === SortActionType.None) {
const type = config?.sort?.type;
if (!type || type === SortActionType.None || type === SortActionType.Customize) {
return null;
}
const isASC = config.sort.type === SortActionType.ASC;
return {
sortFieldId: chartDataSet.getFieldKey(config),
sortFunc: params => {
const { data } = params;
return data?.sort((a, b) =>
isASC ? a?.localeCompare(b) : b?.localeCompare(a),
type === SortActionType.ASC ? a?.localeCompare(b) : b?.localeCompare(a),
);
},
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ const config: ChartConfig = {
options: {
sortable: { backendSort: false },
},
allowFieldCustomizeSort: true,
},
{
label: 'row',
Expand All @@ -37,6 +38,7 @@ const config: ChartConfig = {
},
drillable: false,
drillContextMenuVisible: true,
allowFieldCustomizeSort: true,
},
{
label: 'metrics',
Expand Down
1 change: 1 addition & 0 deletions frontend/src/app/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ export const ChartDataSectionFieldActionType = {
ColorizeSingle: 'colorSingle',
Size: 'size',
DateLevel: 'dateLevel',
CustomizeSort: 'customizeSort',
};

export const FilterRelationType = {
Expand Down
19 changes: 14 additions & 5 deletions frontend/src/app/hooks/useFieldActionModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ function useFieldActionModal({ i18nPrefix }: I18NComponentProps) {
};

switch (actionType) {
case ChartDataSectionFieldActionType.CustomizeSort:
return <FieldActions.CustomizeSortAction {...props} />;
case ChartDataSectionFieldActionType.Sortable:
return <FieldActions.SortAction {...props} />;
case ChartDataSectionFieldActionType.Alias:
Expand Down Expand Up @@ -97,11 +99,18 @@ function useFieldActionModal({ i18nPrefix }: I18NComponentProps) {
aggregation?: boolean,
) => {
const currentConfig = dataConfig.rows?.find(c => c.uid === columnUid);
let _modalSize = StateModalSize.MIDDLE;
if (actionType === ChartDataSectionFieldActionType.Colorize) {
_modalSize = StateModalSize.XSMALL;
} else if (actionType === ChartDataSectionFieldActionType.ColorizeSingle) {
_modalSize = StateModalSize.XSMALL;
let _modalSize: StateModalSize;
switch (actionType) {
case ChartDataSectionFieldActionType.Colorize:
case ChartDataSectionFieldActionType.ColorizeSingle:
case ChartDataSectionFieldActionType.Sortable:
_modalSize = StateModalSize.XSMALL;
break;
case ChartDataSectionFieldActionType.CustomizeSort:
_modalSize = StateModalSize.SMALL;
break;
default:
_modalSize = StateModalSize.MIDDLE;
}
return (show as Function)({
title: t(actionType),
Expand Down
15 changes: 12 additions & 3 deletions frontend/src/app/models/ChartDataRequestBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -442,15 +442,24 @@ export class ChartDataRequestBuilder {
return acc;
}, [])
.filter(
col =>
col?.sort?.type &&
[SortActionType.ASC, SortActionType.DESC].includes(col?.sort?.type),
col => {
const type = col?.sort?.type;
if (!type) {
return false;
} else if (type === SortActionType.Customize) {
const value = col.sort!.value;
return Array.isArray(value) && value.length > 0;
} else {
return [SortActionType.ASC, SortActionType.DESC].includes(type);
}
}
);

const originalSorters = sortColumns.map(aggCol => ({
column: this.buildColumnName(aggCol),
operator: aggCol.sort?.type!,
aggOperator: aggCol.aggregate,
value: aggCol.sort?.type === SortActionType.Customize ? aggCol.sort.value! : undefined,
}));

const _extraSorters = this.extraSorters
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ import SubMenu from 'antd/lib/menu/SubMenu';
import {
ChartDataSectionFieldActionType,
ChartDataViewFieldCategory,
DataViewFieldType,
SortActionType,
} from 'app/constants';
import useI18NPrefix from 'app/hooks/useI18NPrefix';
import { ChartDataSectionField } from 'app/types/ChartConfig';
Expand All @@ -30,7 +32,7 @@ import { FC } from 'react';
import AggregationAction from '../ChartFieldAction/AggregationAction';
import AggregationLimitAction from '../ChartFieldAction/AggregationLimitAction';
import DateLevelAction from '../ChartFieldAction/DateLevelAction/DateLevelAction';
import SortAction from '../ChartFieldAction/SortAction';
import { SortAction } from '../ChartFieldAction/SortAction';
import { updateDataConfigByField } from './utils';

const ChartDataConfigSectionActionMenu: FC<
Expand All @@ -53,12 +55,7 @@ const ChartDataConfigSectionActionMenu: FC<
onConfigChanged,
}) => {
const t = useI18NPrefix(`viz.palette.data.enum.actionType`);
const subMenuAction = [
ChartDataSectionFieldActionType.Sortable,
ChartDataSectionFieldActionType.Aggregate,
ChartDataSectionFieldActionType.AggregateLimit,
ChartDataSectionFieldActionType.DateLevel,
];


const handleFieldConfigChanged = (
columnUid: string,
Expand Down Expand Up @@ -131,14 +128,20 @@ const ChartDataConfigSectionActionMenu: FC<
}
const options = config?.options?.[actionName];
if (actionName === ChartDataSectionFieldActionType.Sortable) {
const allowCustomSort = config?.allowFieldCustomizeSort && type !== DataViewFieldType.NUMERIC;
return (
<SortAction
config={fieldConfig}
onConfigChange={(config, needRefresh) => {
handleFieldConfigChanged(uid, config, needRefresh);
if (config.sort?.type === SortActionType.Customize) {
onOpenModal(uid)(ChartDataSectionFieldActionType.CustomizeSort);
} else {
handleFieldConfigChanged(uid, config, needRefresh);
}
}}
options={options}
mode="menu"
allowCustomSort={allowCustomSort}
/>
);
}
Expand Down Expand Up @@ -198,4 +201,10 @@ const ChartDataConfigSectionActionMenu: FC<
);
};

const subMenuAction = [
ChartDataSectionFieldActionType.Sortable,
ChartDataSectionFieldActionType.Aggregate,
ChartDataSectionFieldActionType.AggregateLimit,
ChartDataSectionFieldActionType.DateLevel,
];
export default ChartDataConfigSectionActionMenu;
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import {
GroupOutlined,
SortAscendingOutlined,
SortDescendingOutlined,
DragOutlined,
} from '@ant-design/icons';
import Dropdown from 'antd/lib/dropdown';
import { SortActionType } from 'app/constants';
Expand Down Expand Up @@ -137,6 +138,9 @@ const ChartDraggableElementField: FC<{
if (col.sort.type === SortActionType.DESC) {
icons.push(<SortDescendingOutlined key="sort" />);
}
if (col.sort.type === SortActionType.Customize) {
icons.push(<DragOutlined key="sort" />);
}
}
if (col.format) {
icons.push(<FormatPainterOutlined key="format" />);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {
ChartDataSectionType,
ChartDataViewFieldCategory,
DataViewFieldType,
SortActionType,
} from 'app/constants';
import ChartDrillContext from 'app/contexts/ChartDrillContext';
import useFieldActionModal from 'app/hooks/useFieldActionModal';
Expand All @@ -29,7 +30,7 @@ import ChartDatasetContext from 'app/pages/ChartWorkbenchPage/contexts/ChartData
import VizDataViewContext from 'app/pages/ChartWorkbenchPage/contexts/ChartDataViewContext';
import { ChartDataSectionField } from 'app/types/ChartConfig';
import { ChartDataConfigSectionProps } from 'app/types/ChartDataConfigSection';
import { getColumnRenderName } from 'app/utils/chartHelper';
import { getColumnRenderName, removeCustomizeSortConfig } from 'app/utils/chartHelper';
import { reachLowerBoundCount } from 'app/utils/internalChartHelper';
import { updateBy, updateByKey } from 'app/utils/mutation';
import { CHART_DRAG_ELEMENT_TYPE } from 'globalConstants';
Expand All @@ -51,7 +52,9 @@ import { uuidv4 } from 'utils/utils';
import ChartDraggableElement from './ChartDraggableElement';
import ChartDraggableElementField from './ChartDraggableElementField';
import ChartDraggableElementHierarchy from './ChartDraggableElementHierarchy';
import { getDefaultAggregate, updateDataConfigByField } from './utils';
import {getDefaultAggregate, isUpdate2CustomizeSort, updateDataConfigByField} from './utils';
import { useSelector } from 'react-redux';
import { chartConfigSelector } from 'app/pages/ChartWorkbenchPage/slice/selectors';

type DragItem = {
index?: number;
Expand All @@ -65,6 +68,7 @@ export const ChartDraggableTargetContainer: FC<ChartDataConfigSectionProps> =
translate: t = (...args) => args?.[0],
onConfigChanged,
}) {
const chartConfig = useSelector(chartConfigSelector);
const { dataset } = useContext(ChartDatasetContext);
const { drillOption } = useContext(ChartDrillContext);
const { dataView, availableSourceFunctions } =
Expand Down Expand Up @@ -357,12 +361,32 @@ export const ChartDraggableTargetContainer: FC<ChartDataConfigSectionProps> =
if (!fieldConfig) {
return;
}
const newConfig = updateDataConfigByField(
let _currentConfig = currentConfig;
// Check whether the sort configuration of field has been changed to custom sort
const _isUpdate2CustomizeSort = isUpdate2CustomizeSort(columnUid, _currentConfig, fieldConfig);
if (_isUpdate2CustomizeSort) {
// Only allows custom sorting on a single field
// case 1: Custom sort field already exists in the different config section
chartConfig?.datas?.forEach((item, index) => {
if (item.key !== _currentConfig.key
&& item.allowFieldCustomizeSort
&& item.rows?.some(r => r?.sort?.type === SortActionType.Customize)
) {
// remove the custom sort configuration of field
onConfigChanged?.([index], removeCustomizeSortConfig(item), false);
}
});
// case 2: Custom sort field already exists in the same config section
if (_currentConfig.rows?.some(r => r?.sort?.type === SortActionType.Customize)) {
_currentConfig = removeCustomizeSortConfig(_currentConfig);
}
}
_currentConfig = updateDataConfigByField(
columnUid,
currentConfig,
fieldConfig,
);
onConfigChanged?.(ancestors, newConfig, needRefresh);
onConfigChanged?.(ancestors, _currentConfig, _isUpdate2CustomizeSort || needRefresh);
};

const handleOpenActionModal =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import {
AggregateFieldSubAggregateType,
ChartDataSectionFieldActionType,
ChartDataSectionType,
ChartDataViewFieldCategory,
ChartDataViewFieldCategory, SortActionType,
} from 'app/constants';
import { ChartDataConfig, ChartDataSectionField } from 'app/types/ChartConfig';
import { updateBy } from 'app/utils/mutation';
Expand Down Expand Up @@ -83,3 +83,13 @@ export const getDefaultAggregate = (
}
}
};

export const isUpdate2CustomizeSort = (
uid: string,
config: ChartDataConfig,
field: ChartDataSectionField,
) => {
const originField = config?.rows?.find(r => r.uid === uid);
const type = field?.sort?.type;
return type === SortActionType.Customize && originField?.sort?.type !== type;
}
Loading