This is an automated email from the ASF dual-hosted git repository. sushuang pushed a commit to branch dataset-trans in repository https://gitbox.apache.org/repos/asf/incubator-echarts.git
commit f8a59dfa6b2c9f651582c053e851d5286afc7a63 Author: 100pah <sushuang0...@gmail.com> AuthorDate: Fri Jul 31 16:20:01 2020 +0800 fix: fix dimension inherit rule. --- src/component/transform/filterTransform.ts | 6 +- src/component/transform/sortTransform.ts | 6 +- src/data/Source.ts | 22 ++++- src/data/helper/sourceHelper.ts | 22 +++-- src/data/helper/sourceManager.ts | 45 ++++++--- src/data/helper/transform.ts | 74 ++++++++++----- test/data-transform.html | 145 ++++++++++++++++++++++++++++- 7 files changed, 264 insertions(+), 56 deletions(-) diff --git a/src/component/transform/filterTransform.ts b/src/component/transform/filterTransform.ts index 4b13008..4126f98 100644 --- a/src/component/transform/filterTransform.ts +++ b/src/component/transform/filterTransform.ts @@ -65,7 +65,7 @@ export const filterTransform: ExternalDataTransform<FilterTransformOption> = { if (__DEV__) { errMsg = makePrintable( 'Can not find dimension info via: "' + dimLoose + '".\n', - 'Existing dimensions: ', source.dimensions, '.\n', + 'Existing dimensions: ', source.getDimensionInfoAll(), '.\n', 'Illegal condition:', exprOption, '.\n' ); } @@ -93,9 +93,7 @@ export const filterTransform: ExternalDataTransform<FilterTransformOption> = { } return { - data: resultData, - dimensions: source.dimensions, - sourceHeader: sourceHeaderCount + data: resultData }; } }; diff --git a/src/component/transform/sortTransform.ts b/src/component/transform/sortTransform.ts index 9dfce97..e4f75e5 100644 --- a/src/component/transform/sortTransform.ts +++ b/src/component/transform/sortTransform.ts @@ -124,7 +124,7 @@ export const sortTransform: ExternalDataTransform<SortTransformOption> = { if (__DEV__) { errMsg = makePrintable( 'Can not find dimension info via: "' + dimLoose + '".\n', - 'Existing dimensions: ', source.dimensions, '.\n', + 'Existing dimensions: ', source.getDimensionInfoAll(), '.\n', 'Illegal config:', orderExpr, '.\n' ); } @@ -214,9 +214,7 @@ export const sortTransform: ExternalDataTransform<SortTransformOption> = { } return { - data: resultData, - dimensions: source.dimensions, - sourceHeader: sourceHeaderCount + data: resultData }; } }; diff --git a/src/data/Source.ts b/src/data/Source.ts index bed37d6..06d5a27 100644 --- a/src/data/Source.ts +++ b/src/data/Source.ts @@ -17,16 +17,18 @@ * under the License. */ -import {createHashMap, isTypedArray, HashMap} from 'zrender/src/core/util'; +import {isTypedArray, HashMap} from 'zrender/src/core/util'; import { SourceFormat, SeriesLayoutBy, DimensionDefinition, - OptionEncodeValue, OptionSourceData, OptionEncode, + OptionEncodeValue, OptionSourceData, SOURCE_FORMAT_ORIGINAL, SERIES_LAYOUT_BY_COLUMN, SOURCE_FORMAT_UNKNOWN, SOURCE_FORMAT_KEYED_COLUMNS, SOURCE_FORMAT_TYPED_ARRAY, - DimensionName + DimensionName, + OptionSourceHeader, + DimensionDefinitionLoose } from '../util/types'; /** @@ -65,6 +67,12 @@ import { * + "unknown" */ +export interface SourceMetaRawOption { + seriesLayoutBy: SeriesLayoutBy; + sourceHeader: OptionSourceHeader; + dimensions: DimensionDefinitionLoose[]; +} + class Source { /** @@ -107,6 +115,11 @@ class Source { */ readonly dimensionsDetectCount: number; + /** + * Raw props from user option. + */ + readonly metaRawOption: SourceMetaRawOption; + constructor(fields: { data: OptionSourceData, @@ -118,6 +131,8 @@ class Source { startIndex?: number, // default: 0 dimensionsDetectCount?: number, + metaRawOption?: SourceMetaRawOption, + // [Caveat] // This is the raw user defined `encode` in `series`. // If user not defined, DO NOT make a empty object or hashMap here. @@ -136,6 +151,7 @@ class Source { this.dimensionsDefine = fields.dimensionsDefine; this.dimensionsDetectCount = fields.dimensionsDetectCount; this.encodeDefine = fields.encodeDefine; + this.metaRawOption = fields.metaRawOption; } /** diff --git a/src/data/helper/sourceHelper.ts b/src/data/helper/sourceHelper.ts index 9ae0151..0905908 100644 --- a/src/data/helper/sourceHelper.ts +++ b/src/data/helper/sourceHelper.ts @@ -33,9 +33,10 @@ import { hasOwn, HashMap, isNumber, - clone + clone, + defaults } from 'zrender/src/core/util'; -import Source from '../Source'; +import Source, { SourceMetaRawOption } from '../Source'; import { SOURCE_FORMAT_ORIGINAL, @@ -90,12 +91,6 @@ type SeriesEncodeInternal = { [key in keyof OptionEncode]: DimensionIndex[]; }; -export interface SourceMetaRawOption { - seriesLayoutBy: SeriesLayoutBy; - sourceHeader: OptionSourceHeader; - dimensions: DimensionDefinitionLoose[]; -} - export function detectSourceFormat(data: DatasetOption['source']): SourceFormat { let sourceFormat: SourceFormat = SOURCE_FORMAT_UNKNOWN; @@ -170,12 +165,21 @@ export function createSource( dimensionsDefine: dimInfo.dimensionsDefine, startIndex: dimInfo.startIndex, dimensionsDetectCount: dimInfo.dimensionsDetectCount, - encodeDefine: makeEncodeDefine(encodeDefine) + encodeDefine: makeEncodeDefine(encodeDefine), + metaRawOption: clone(thisMetaRawOption) }); return source; } +// See [DIMENSION_INHERIT_RULE] in `sourceManager.ts`. +export function inheritSourceMetaRawOption(opt: { + parent: SourceMetaRawOption, // Can be null/undefined + thisNew: SourceMetaRawOption // Must be object +}) { + return defaults(opt.thisNew, opt.parent); +} + /** * Clone except source data. */ diff --git a/src/data/helper/sourceManager.ts b/src/data/helper/sourceManager.ts index 74ed264..5a3f9ea 100644 --- a/src/data/helper/sourceManager.ts +++ b/src/data/helper/sourceManager.ts @@ -28,7 +28,7 @@ import { } from '../../util/types'; import { querySeriesUpstreamDatasetModel, queryDatasetUpstreamDatasetModels, - createSource, SourceMetaRawOption, cloneSourceShallow + createSource, SourceMetaRawOption, cloneSourceShallow, inheritSourceMetaRawOption } from './sourceHelper'; import { applyDataTransform } from './transform'; @@ -53,6 +53,27 @@ import { applyDataTransform } from './transform'; * They will not be calculated until `source` is about to be visited (to prevent from * duplicate calcuation). `source` is visited only in series and input to transforms. * + * [DIMENSION_INHERIT_RULE]: + * By default the dimensions are inherited from ancestors, unless a transform return + * a new dimensions definition. + * Consider the case: + * ```js + * dataset: [{ + * source: [ ['Product', 'Sales', 'Prise'], ['Cookies', 321, 44.21], ...] + * }, { + * transform: { type: 'filter', ... } + * }] + * + * dataset: [{ + * dimension: ['Product', 'Sales', 'Prise'], + * source: [ ['Cookies', 321, 44.21], ...] + * }, { + * transform: { type: 'filter', ... } + * }] + * ``` + * The two types of option should have the same behavior after transform. + * + * * [SCENARIO]: * (1) Provide source data directly: * ```js @@ -172,16 +193,15 @@ export class SourceManager { const seriesModel = sourceHost as SeriesEncodableModel; let data; let sourceFormat: SourceFormat; - let upMetaRawOption; + let upSource; // Has upstream dataset if (hasUpstream) { const upSourceMgr = upSourceMgrList[0]; upSourceMgr.prepareSource(); - const upSource = upSourceMgr.getSource(); + upSource = upSourceMgr.getSource(); data = upSource.data; sourceFormat = upSource.sourceFormat; - upMetaRawOption = upSourceMgr._getSourceMetaRawOption(); upstreamSignList = [upSourceMgr._getVersionSign()]; } // Series data is from own. @@ -192,11 +212,12 @@ export class SourceManager { upstreamSignList = []; } - const thisMetaRawOption = defaults( - this._getSourceMetaRawOption(), - // See [REQUIREMENT MEMO], merge settings on series and parent dataset if it is root. - upMetaRawOption - ); + // See [REQUIREMENT_MEMO], merge settings on series and parent dataset if it is root. + const thisMetaRawOption = inheritSourceMetaRawOption({ + parent: upSource ? upSource.metaRawOption : null, + thisNew: this._createSourceMetaRawOption() + }); + resultSourceList = [createSource( data, thisMetaRawOption, @@ -218,7 +239,7 @@ export class SourceManager { const sourceData = datasetModel.get('source', true); resultSourceList = [createSource( sourceData, - this._getSourceMetaRawOption(), + this._createSourceMetaRawOption(), null, // Note: dataset option does not have `encode`. null @@ -329,7 +350,7 @@ export class SourceManager { } } - private _getSourceMetaRawOption(): SourceMetaRawOption { + private _createSourceMetaRawOption(): SourceMetaRawOption { const sourceHost = this._sourceHost; let seriesLayoutBy: SeriesLayoutBy; let sourceHeader: OptionSourceHeader; @@ -339,7 +360,7 @@ export class SourceManager { sourceHeader = sourceHost.get('sourceHeader', true); dimensions = sourceHost.get('dimensions', true); } - // See [REQUIREMENT MEMO], `non-root-dataset` do not support them. + // See [REQUIREMENT_MEMO], `non-root-dataset` do not support them. else if (!this._getUpstreamSourceManagers().length) { const model = sourceHost as DatasetModel; seriesLayoutBy = model.get('seriesLayoutBy', true); diff --git a/src/data/helper/transform.ts b/src/data/helper/transform.ts index e6150b1..d657d52 100644 --- a/src/data/helper/transform.ts +++ b/src/data/helper/transform.ts @@ -32,7 +32,7 @@ import { getRawSourceItemGetter, getRawSourceDataCounter, getRawSourceValueGetter } from './dataProvider'; import { parseDataValue } from './parseDataValue'; -import { createSource } from './sourceHelper'; +import { createSource, inheritSourceMetaRawOption } from './sourceHelper'; import { consoleLog, makePrintable } from '../../util/log'; @@ -76,7 +76,7 @@ export interface ExternalDataTransformResultItem { dimensions?: DimensionDefinitionLoose[]; sourceHeader?: OptionSourceHeader; } -export interface ExternalDimensionDefinition extends DimensionDefinition { +interface ExternalDimensionDefinition extends DimensionDefinition { // Mandatory index: DimensionIndex; } @@ -95,13 +95,16 @@ class ExternalSource { data: OptionSourceData; sourceFormat: SourceFormat; - dimensions: ExternalDimensionDefinition[]; sourceHeaderCount: number; getDimensionInfo(dim: DimensionLoose): ExternalDimensionDefinition { return; } + getDimensionInfoAll(): ExternalDimensionDefinition[] { + return; + } + getRawDataItem(dataIndex: number): OptionDataItem { return; } @@ -140,8 +143,13 @@ function createExternalSource( extSource.sourceFormat = sourceFormat; extSource.sourceHeaderCount = sourceHeaderCount; + // [MEMO] // Create a new dimensions structure for exposing. - const dimensions = extSource.dimensions = [] as ExternalDimensionDefinition[]; + // Do not expose all dimension info to users directly. + // Becuase the dimension is probably auto detected from data and not might reliable. + // Should not lead the transformers to think that is relialbe and return it. + // See [DIMENSION_INHERIT_RULE] in `sourceManager.ts`. + const dimensions = [] as ExternalDimensionDefinition[]; const dimsByName = {} as Dictionary<ExternalDimensionDefinition>; each(dimsDef, function (dimDef, idx) { const name = dimDef.name; @@ -179,7 +187,7 @@ function createExternalSource( if (rawItem == null) { return; } - const dimDef = extSource.dimensions[dimIndex]; + const dimDef = dimensions[dimIndex]; // When `dimIndex` is `null`, `rawValueGetter` return the whole item. if (dimDef) { return rawValueGetter(rawItem, dimIndex, dimDef.name) as OptionDataValue; @@ -187,6 +195,7 @@ function createExternalSource( }; extSource.getDimensionInfo = bind(getDimensionInfo, null, dimensions, dimsByName); + extSource.getDimensionInfoAll = bind(getDimensionInfoAll, null, dimensions); return extSource; } @@ -212,6 +221,12 @@ function getDimensionInfo( } } +function getDimensionInfoAll( + dimensions: ExternalDimensionDefinition[] +): ExternalDimensionDefinition[] { + return dimensions; +} + const externalTransformMap = createHashMap<ExternalDataTransform, string>(); @@ -241,22 +256,13 @@ export function applyDataTransform( for (let i = 0, len = pipedTransOption.length; i < len; i++) { const transOption = pipedTransOption[i]; - sourceList = applySingleDataTransform(transOption, sourceList); + const isFinal = i === len - 1; + sourceList = applySingleDataTransform(transOption, sourceList, infoForPrint, isFinal); // piped transform only support single input, except the fist one. // piped transform only support single output, except the last one. - if (i < len - 1) { + if (!isFinal) { sourceList.length = Math.max(sourceList.length, 1); } - - if (__DEV__) { - if (transOption.print) { - const printStrArr = map(sourceList, source => { - return '--- datasetIndex: ' + infoForPrint.datasetIndex + ', transform result: ---\n' - + makePrintable(source.data); - }).join('\n'); - consoleLog(printStrArr); - } - } } return sourceList; @@ -264,7 +270,9 @@ export function applyDataTransform( function applySingleDataTransform( rawTransOption: DataTransformOption, - upSourceList: Source[] + upSourceList: Source[], + infoForPrint: { datasetIndex: number }, + isFinal: boolean ): Source[] { assert(upSourceList.length, 'Must have at least one upstream dataset.'); @@ -292,6 +300,23 @@ function applySingleDataTransform( }) ); + if (__DEV__) { + if (isFinal && transOption.print) { + const printStrArr = map(resultList, extSource => { + return [ + '--- datasetIndex: ' + infoForPrint.datasetIndex + '---', + '- transform result data:', + makePrintable(extSource.data), + '- transform result dimensions:', + makePrintable(extSource.dimensions), + '- transform result sourceHeader: ' + extSource.sourceHeader, + '------' + ].join('\n'); + }).join('\n'); + consoleLog(printStrArr); + } + } + return map(resultList, function (result) { assert( isObject(result), @@ -302,13 +327,18 @@ function applySingleDataTransform( 'Result data should be object or array in data transform.' ); - return createSource( - result.data, - { + const resultMetaRawOption = inheritSourceMetaRawOption({ + parent: upSourceList[0].metaRawOption, + thisNew: { seriesLayoutBy: SERIES_LAYOUT_BY_COLUMN, sourceHeader: result.sourceHeader, dimensions: result.dimensions - }, + } + }); + + return createSource( + result.data, + resultMetaRawOption, null, null ); diff --git a/test/data-transform.html b/test/data-transform.html index 05ada21..9a11dc7 100644 --- a/test/data-transform.html +++ b/test/data-transform.html @@ -37,10 +37,12 @@ under the License. - <!-- <div id="main_simplest_pies"></div> + <div id="main_simplest_pies"></div> <div id="main_pies_encode_price"></div> - <div id="main_cartesian_parse_trim_time_reg"></div> --> + <div id="main_cartesian_parse_trim_time_reg"></div> <div id="main_cartesian_sort"></div> + <div id="main_update_condition"></div> + <div id="main_update_source_no_dim_inside_data"></div> @@ -656,6 +658,145 @@ under the License. + + + <script> + require(['echarts'], function (echarts) { + var currentYear = 2011 + var option = { + title: { + text: currentYear, + left: 'center' + }, + dataset: [{ + source: FOOD_SALES_PRICE_WITH_HEADER + }, { + id: 'one_year', + transform: { + type: 'filter', + config: { dimension: 'Year', value: currentYear } + } + }], + tooltip: {}, + xAxis: { type: 'category' }, + yAxis: {}, + series: [{ + name: 'one year', + type: 'bar', + datasetIndex: 1, + }] + }; + + var chart = testHelper.create(echarts, 'main_update_condition', { + title: [ + 'click "next year", check the bar change.' + ], + height: 300, + option: option, + buttons: [{ + text: 'next year', + onclick: function () { + currentYear++; + if (currentYear >= 2014) { + currentYear = 2011 + } + chart.setOption({ + title: { + text: currentYear + }, + dataset: { + id: 'one_year', + transform: { + type: 'filter', + config: { dimension: 'Year', value: currentYear } + } + } + }); + } + }] + }); + }); + </script> + + + + + + <script> + require(['echarts'], function (echarts) { + var currData = FOOD_SALES_PRICE_NO_HEADER; + + var option = { + dataset: [{ + id: 'all_data', + dimensions: FOOD_SALES_PRICE_HEADER, + source: currData + }, { + transform: { + type: 'filter', + print: true, + config: { dimension: 'Price', '<': 40 } + } + }], + tooltip: {}, + legend: {}, + xAxis: { type: 'category' }, + yAxis: { scale: true }, + series: [{ + name: 'all data', + type: 'scatter', + symbolSize: 15, + encode: { + itemId: 'Product', + y: 'Price', + label: [0, 1, 2, 3] + }, + itemStyle: { + color: '#999' + } + }, { + name: 'Price < 40', + type: 'scatter', + encode: { + itemId: 'Product', + x: 0, + y: 'Price', + label: [0, 1, 2, 3] + }, + datasetIndex: 1 + }] + }; + + var chart = testHelper.create(echarts, 'main_update_source_no_dim_inside_data', { + title: [ + 'Init: all items that Price < 40', + 'click "add price 10", check the bar change.' + ], + height: 300, + option: option, + buttons: [{ + text: 'add price 10', + onclick: function () { + currData = echarts.util.clone(currData); + echarts.util.each(currData, function (line) { + line[2] += 10; + }); + + chart.setOption({ + dataset: { + id: 'all_data', + source: currData + } + }); + } + }] + }); + }); + </script> + + + + </body> </html> --------------------------------------------------------------------- To unsubscribe, e-mail: commits-unsubscr...@echarts.apache.org For additional commands, e-mail: commits-h...@echarts.apache.org