blob: dbe246c6933ef16c3d4c9869ad06b01c3000af35 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 {
DataTransformOption, ExternalDataTransform, ExternalDataTransformResultItem
} from '../../data/helper/transform';
import {
DimensionLoose, DimensionIndex, OptionDataValue, SOURCE_FORMAT_ARRAY_ROWS, SOURCE_FORMAT_OBJECT_ROWS
} from '../../util/types';
import { makePrintable, throwError } from '../../util/log';
import { each } from 'zrender/src/core/util';
import { normalizeToArray } from '../../util/model';
import {
RawValueParserType, getRawValueParser, SortOrderComparator
} from '../../data/helper/dataValueHelper';
/**
* @usage
*
* ```js
* transform: {
* type: 'sort',
* config: { dimension: 'score', order: 'asc' }
* }
* transform: {
* type: 'sort',
* config: [
* { dimension: 1, order: 'asc' },
* { dimension: 'age', order: 'desc' }
* ]
* }
* ```
*/
export interface SortTransformOption extends DataTransformOption {
type: 'sort';
config: OrderExpression | OrderExpression[];
}
// PENDING: whether support { dimension: 'score', order: 'asc' } ?
type OrderExpression = {
dimension: DimensionLoose;
order: 'asc' | 'desc';
parser?: RawValueParserType;
// The meansing of "incomparable": see [SORT_COMPARISON_RULE]
// in `data/helper/dataValueHelper.ts`
incomparable?: 'min' | 'max';
};
let sampleLog = '';
if (__DEV__) {
sampleLog = [
'Valid config is like:',
'{ dimension: "age", order: "asc" }',
'or [{ dimension: "age", order: "asc"], { dimension: "date", order: "desc" }]'
].join(' ');
}
export const sortTransform: ExternalDataTransform<SortTransformOption> = {
type: 'echarts:sort',
transform: function (params) {
const upstream = params.upstream;
const config = params.config;
let errMsg = '';
// Normalize
// const orderExprList: OrderExpression[] = isArray(config[0])
// ? config as OrderExpression[]
// : [config as OrderExpression];
const orderExprList: OrderExpression[] = normalizeToArray(config);
if (!orderExprList.length) {
if (__DEV__) {
errMsg = 'Empty `config` in sort transform.';
}
throwError(errMsg);
}
const orderDefList: {
dimIdx: DimensionIndex;
parser: ReturnType<typeof getRawValueParser>;
comparator: SortOrderComparator
}[] = [];
each(orderExprList, function (orderExpr) {
const dimLoose = orderExpr.dimension;
const order = orderExpr.order;
const parserName = orderExpr.parser;
const incomparable = orderExpr.incomparable;
if (dimLoose == null) {
if (__DEV__) {
errMsg = 'Sort transform config must has "dimension" specified.' + sampleLog;
}
throwError(errMsg);
}
if (order !== 'asc' && order !== 'desc') {
if (__DEV__) {
errMsg = 'Sort transform config must has "order" specified.' + sampleLog;
}
throwError(errMsg);
}
if (incomparable && (incomparable !== 'min' && incomparable !== 'max')) {
let errMsg = '';
if (__DEV__) {
errMsg = 'incomparable must be "min" or "max" rather than "' + incomparable + '".';
}
throwError(errMsg);
}
if (order !== 'asc' && order !== 'desc') {
let errMsg = '';
if (__DEV__) {
errMsg = 'order must be "asc" or "desc" rather than "' + order + '".';
}
throwError(errMsg);
}
const dimInfo = upstream.getDimensionInfo(dimLoose);
if (!dimInfo) {
if (__DEV__) {
errMsg = makePrintable(
'Can not find dimension info via: ' + dimLoose + '.\n',
'Existing dimensions: ', upstream.cloneAllDimensionInfo(), '.\n',
'Illegal config:', orderExpr, '.\n'
);
}
throwError(errMsg);
}
const parser = parserName ? getRawValueParser(parserName) : null;
if (parserName && !parser) {
if (__DEV__) {
errMsg = makePrintable(
'Invalid parser name ' + parserName + '.\n',
'Illegal config:', orderExpr, '.\n'
);
}
throwError(errMsg);
}
orderDefList.push({
dimIdx: dimInfo.index,
parser: parser,
comparator: new SortOrderComparator(order, incomparable)
});
});
// TODO: support it?
const sourceFormat = upstream.sourceFormat;
if (sourceFormat !== SOURCE_FORMAT_ARRAY_ROWS
&& sourceFormat !== SOURCE_FORMAT_OBJECT_ROWS
) {
if (__DEV__) {
errMsg = 'sourceFormat "' + sourceFormat + '" is not supported yet';
}
throwError(errMsg);
}
// Other upstream format are all array.
const resultData = [];
for (let i = 0, len = upstream.count(); i < len; i++) {
resultData.push(upstream.getRawDataItem(i));
}
resultData.sort(function (item0, item1) {
for (let i = 0; i < orderDefList.length; i++) {
const orderDef = orderDefList[i];
let val0 = upstream.retrieveValueFromItem(item0, orderDef.dimIdx);
let val1 = upstream.retrieveValueFromItem(item1, orderDef.dimIdx);
if (orderDef.parser) {
val0 = orderDef.parser(val0) as OptionDataValue;
val1 = orderDef.parser(val1) as OptionDataValue;
}
const result = orderDef.comparator.evaluate(val0, val1);
if (result !== 0) {
return result;
}
}
return 0;
});
return {
data: resultData as ExternalDataTransformResultItem['data']
};
}
};