sourceManager.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475
  1. /*
  2. * Licensed to the Apache Software Foundation (ASF) under one
  3. * or more contributor license agreements. See the NOTICE file
  4. * distributed with this work for additional information
  5. * regarding copyright ownership. The ASF licenses this file
  6. * to you under the Apache License, Version 2.0 (the
  7. * "License"); you may not use this file except in compliance
  8. * with the License. You may obtain a copy of the License at
  9. *
  10. * http://www.apache.org/licenses/LICENSE-2.0
  11. *
  12. * Unless required by applicable law or agreed to in writing,
  13. * software distributed under the License is distributed on an
  14. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  15. * KIND, either express or implied. See the License for the
  16. * specific language governing permissions and limitations
  17. * under the License.
  18. */
  19. /**
  20. * AUTO-GENERATED FILE. DO NOT MODIFY.
  21. */
  22. /*
  23. * Licensed to the Apache Software Foundation (ASF) under one
  24. * or more contributor license agreements. See the NOTICE file
  25. * distributed with this work for additional information
  26. * regarding copyright ownership. The ASF licenses this file
  27. * to you under the Apache License, Version 2.0 (the
  28. * "License"); you may not use this file except in compliance
  29. * with the License. You may obtain a copy of the License at
  30. *
  31. * http://www.apache.org/licenses/LICENSE-2.0
  32. *
  33. * Unless required by applicable law or agreed to in writing,
  34. * software distributed under the License is distributed on an
  35. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  36. * KIND, either express or implied. See the License for the
  37. * specific language governing permissions and limitations
  38. * under the License.
  39. */
  40. import { setAsPrimitive, map, isTypedArray, assert, each, retrieve2 } from 'zrender/lib/core/util.js';
  41. import { createSource, cloneSourceShallow } from '../Source.js';
  42. import { SOURCE_FORMAT_TYPED_ARRAY, SOURCE_FORMAT_ORIGINAL } from '../../util/types.js';
  43. import { querySeriesUpstreamDatasetModel, queryDatasetUpstreamDatasetModels } from './sourceHelper.js';
  44. import { applyDataTransform } from './transform.js';
  45. import DataStore from '../DataStore.js';
  46. import { DefaultDataProvider } from './dataProvider.js';
  47. /**
  48. * [REQUIREMENT_MEMO]:
  49. * (0) `metaRawOption` means `dimensions`/`sourceHeader`/`seriesLayoutBy` in raw option.
  50. * (1) Keep support the feature: `metaRawOption` can be specified both on `series` and
  51. * `root-dataset`. Them on `series` has higher priority.
  52. * (2) Do not support to set `metaRawOption` on a `non-root-dataset`, because it might
  53. * confuse users: whether those props indicate how to visit the upstream source or visit
  54. * the transform result source, and some transforms has nothing to do with these props,
  55. * and some transforms might have multiple upstream.
  56. * (3) Transforms should specify `metaRawOption` in each output, just like they can be
  57. * declared in `root-dataset`.
  58. * (4) At present only support visit source in `SERIES_LAYOUT_BY_COLUMN` in transforms.
  59. * That is for reducing complexity in transforms.
  60. * PENDING: Whether to provide transposition transform?
  61. *
  62. * [IMPLEMENTAION_MEMO]:
  63. * "sourceVisitConfig" are calculated from `metaRawOption` and `data`.
  64. * They will not be calculated until `source` is about to be visited (to prevent from
  65. * duplicate calcuation). `source` is visited only in series and input to transforms.
  66. *
  67. * [DIMENSION_INHERIT_RULE]:
  68. * By default the dimensions are inherited from ancestors, unless a transform return
  69. * a new dimensions definition.
  70. * Consider the case:
  71. * ```js
  72. * dataset: [{
  73. * source: [ ['Product', 'Sales', 'Prise'], ['Cookies', 321, 44.21], ...]
  74. * }, {
  75. * transform: { type: 'filter', ... }
  76. * }]
  77. * dataset: [{
  78. * dimension: ['Product', 'Sales', 'Prise'],
  79. * source: [ ['Cookies', 321, 44.21], ...]
  80. * }, {
  81. * transform: { type: 'filter', ... }
  82. * }]
  83. * ```
  84. * The two types of option should have the same behavior after transform.
  85. *
  86. *
  87. * [SCENARIO]:
  88. * (1) Provide source data directly:
  89. * ```js
  90. * series: {
  91. * encode: {...},
  92. * dimensions: [...]
  93. * seriesLayoutBy: 'row',
  94. * data: [[...]]
  95. * }
  96. * ```
  97. * (2) Series refer to dataset.
  98. * ```js
  99. * series: [{
  100. * encode: {...}
  101. * // Ignore datasetIndex means `datasetIndex: 0`
  102. * // and the dimensions defination in dataset is used
  103. * }, {
  104. * encode: {...},
  105. * seriesLayoutBy: 'column',
  106. * datasetIndex: 1
  107. * }]
  108. * ```
  109. * (3) dataset transform
  110. * ```js
  111. * dataset: [{
  112. * source: [...]
  113. * }, {
  114. * source: [...]
  115. * }, {
  116. * // By default from 0.
  117. * transform: { type: 'filter', config: {...} }
  118. * }, {
  119. * // Piped.
  120. * transform: [
  121. * { type: 'filter', config: {...} },
  122. * { type: 'sort', config: {...} }
  123. * ]
  124. * }, {
  125. * id: 'regressionData',
  126. * fromDatasetIndex: 1,
  127. * // Third-party transform
  128. * transform: { type: 'ecStat:regression', config: {...} }
  129. * }, {
  130. * // retrieve the extra result.
  131. * id: 'regressionFormula',
  132. * fromDatasetId: 'regressionData',
  133. * fromTransformResult: 1
  134. * }]
  135. * ```
  136. */
  137. var SourceManager =
  138. /** @class */
  139. function () {
  140. function SourceManager(sourceHost) {
  141. // Cached source. Do not repeat calculating if not dirty.
  142. this._sourceList = [];
  143. this._storeList = []; // version sign of each upstream source manager.
  144. this._upstreamSignList = [];
  145. this._versionSignBase = 0;
  146. this._dirty = true;
  147. this._sourceHost = sourceHost;
  148. }
  149. /**
  150. * Mark dirty.
  151. */
  152. SourceManager.prototype.dirty = function () {
  153. this._setLocalSource([], []);
  154. this._storeList = [];
  155. this._dirty = true;
  156. };
  157. SourceManager.prototype._setLocalSource = function (sourceList, upstreamSignList) {
  158. this._sourceList = sourceList;
  159. this._upstreamSignList = upstreamSignList;
  160. this._versionSignBase++;
  161. if (this._versionSignBase > 9e10) {
  162. this._versionSignBase = 0;
  163. }
  164. };
  165. /**
  166. * For detecting whether the upstream source is dirty, so that
  167. * the local cached source (in `_sourceList`) should be discarded.
  168. */
  169. SourceManager.prototype._getVersionSign = function () {
  170. return this._sourceHost.uid + '_' + this._versionSignBase;
  171. };
  172. /**
  173. * Always return a source instance. Otherwise throw error.
  174. */
  175. SourceManager.prototype.prepareSource = function () {
  176. // For the case that call `setOption` multiple time but no data changed,
  177. // cache the result source to prevent from repeating transform.
  178. if (this._isDirty()) {
  179. this._createSource();
  180. this._dirty = false;
  181. }
  182. };
  183. SourceManager.prototype._createSource = function () {
  184. this._setLocalSource([], []);
  185. var sourceHost = this._sourceHost;
  186. var upSourceMgrList = this._getUpstreamSourceManagers();
  187. var hasUpstream = !!upSourceMgrList.length;
  188. var resultSourceList;
  189. var upstreamSignList;
  190. if (isSeries(sourceHost)) {
  191. var seriesModel = sourceHost;
  192. var data = void 0;
  193. var sourceFormat = void 0;
  194. var upSource = void 0; // Has upstream dataset
  195. if (hasUpstream) {
  196. var upSourceMgr = upSourceMgrList[0];
  197. upSourceMgr.prepareSource();
  198. upSource = upSourceMgr.getSource();
  199. data = upSource.data;
  200. sourceFormat = upSource.sourceFormat;
  201. upstreamSignList = [upSourceMgr._getVersionSign()];
  202. } // Series data is from own.
  203. else {
  204. data = seriesModel.get('data', true);
  205. sourceFormat = isTypedArray(data) ? SOURCE_FORMAT_TYPED_ARRAY : SOURCE_FORMAT_ORIGINAL;
  206. upstreamSignList = [];
  207. } // See [REQUIREMENT_MEMO], merge settings on series and parent dataset if it is root.
  208. var newMetaRawOption = this._getSourceMetaRawOption() || {};
  209. var upMetaRawOption = upSource && upSource.metaRawOption || {};
  210. var seriesLayoutBy = retrieve2(newMetaRawOption.seriesLayoutBy, upMetaRawOption.seriesLayoutBy) || null;
  211. var sourceHeader = retrieve2(newMetaRawOption.sourceHeader, upMetaRawOption.sourceHeader); // Note here we should not use `upSource.dimensionsDefine`. Consider the case:
  212. // `upSource.dimensionsDefine` is detected by `seriesLayoutBy: 'column'`,
  213. // but series need `seriesLayoutBy: 'row'`.
  214. var dimensions = retrieve2(newMetaRawOption.dimensions, upMetaRawOption.dimensions); // We share source with dataset as much as possible
  215. // to avoid extra memory cost of high dimensional data.
  216. var needsCreateSource = seriesLayoutBy !== upMetaRawOption.seriesLayoutBy || !!sourceHeader !== !!upMetaRawOption.sourceHeader || dimensions;
  217. resultSourceList = needsCreateSource ? [createSource(data, {
  218. seriesLayoutBy: seriesLayoutBy,
  219. sourceHeader: sourceHeader,
  220. dimensions: dimensions
  221. }, sourceFormat)] : [];
  222. } else {
  223. var datasetModel = sourceHost; // Has upstream dataset.
  224. if (hasUpstream) {
  225. var result = this._applyTransform(upSourceMgrList);
  226. resultSourceList = result.sourceList;
  227. upstreamSignList = result.upstreamSignList;
  228. } // Is root dataset.
  229. else {
  230. var sourceData = datasetModel.get('source', true);
  231. resultSourceList = [createSource(sourceData, this._getSourceMetaRawOption(), null)];
  232. upstreamSignList = [];
  233. }
  234. }
  235. if (process.env.NODE_ENV !== 'production') {
  236. assert(resultSourceList && upstreamSignList);
  237. }
  238. this._setLocalSource(resultSourceList, upstreamSignList);
  239. };
  240. SourceManager.prototype._applyTransform = function (upMgrList) {
  241. var datasetModel = this._sourceHost;
  242. var transformOption = datasetModel.get('transform', true);
  243. var fromTransformResult = datasetModel.get('fromTransformResult', true);
  244. if (process.env.NODE_ENV !== 'production') {
  245. assert(fromTransformResult != null || transformOption != null);
  246. }
  247. if (fromTransformResult != null) {
  248. var errMsg = '';
  249. if (upMgrList.length !== 1) {
  250. if (process.env.NODE_ENV !== 'production') {
  251. errMsg = 'When using `fromTransformResult`, there should be only one upstream dataset';
  252. }
  253. doThrow(errMsg);
  254. }
  255. }
  256. var sourceList;
  257. var upSourceList = [];
  258. var upstreamSignList = [];
  259. each(upMgrList, function (upMgr) {
  260. upMgr.prepareSource();
  261. var upSource = upMgr.getSource(fromTransformResult || 0);
  262. var errMsg = '';
  263. if (fromTransformResult != null && !upSource) {
  264. if (process.env.NODE_ENV !== 'production') {
  265. errMsg = 'Can not retrieve result by `fromTransformResult`: ' + fromTransformResult;
  266. }
  267. doThrow(errMsg);
  268. }
  269. upSourceList.push(upSource);
  270. upstreamSignList.push(upMgr._getVersionSign());
  271. });
  272. if (transformOption) {
  273. sourceList = applyDataTransform(transformOption, upSourceList, {
  274. datasetIndex: datasetModel.componentIndex
  275. });
  276. } else if (fromTransformResult != null) {
  277. sourceList = [cloneSourceShallow(upSourceList[0])];
  278. }
  279. return {
  280. sourceList: sourceList,
  281. upstreamSignList: upstreamSignList
  282. };
  283. };
  284. SourceManager.prototype._isDirty = function () {
  285. if (this._dirty) {
  286. return true;
  287. } // All sourceList is from the some upstream.
  288. var upSourceMgrList = this._getUpstreamSourceManagers();
  289. for (var i = 0; i < upSourceMgrList.length; i++) {
  290. var upSrcMgr = upSourceMgrList[i];
  291. if ( // Consider the case that there is ancestor diry, call it recursively.
  292. // The performance is probably not an issue because usually the chain is not long.
  293. upSrcMgr._isDirty() || this._upstreamSignList[i] !== upSrcMgr._getVersionSign()) {
  294. return true;
  295. }
  296. }
  297. };
  298. /**
  299. * @param sourceIndex By default 0, means "main source".
  300. * In most cases there is only one source.
  301. */
  302. SourceManager.prototype.getSource = function (sourceIndex) {
  303. sourceIndex = sourceIndex || 0;
  304. var source = this._sourceList[sourceIndex];
  305. if (!source) {
  306. // Series may share source instance with dataset.
  307. var upSourceMgrList = this._getUpstreamSourceManagers();
  308. return upSourceMgrList[0] && upSourceMgrList[0].getSource(sourceIndex);
  309. }
  310. return source;
  311. };
  312. /**
  313. *
  314. * Get a data store which can be shared across series.
  315. * Only available for series.
  316. *
  317. * @param seriesDimRequest Dimensions that are generated in series.
  318. * Should have been sorted by `storeDimIndex` asc.
  319. */
  320. SourceManager.prototype.getSharedDataStore = function (seriesDimRequest) {
  321. if (process.env.NODE_ENV !== 'production') {
  322. assert(isSeries(this._sourceHost), 'Can only call getDataStore on series source manager.');
  323. }
  324. var schema = seriesDimRequest.makeStoreSchema();
  325. return this._innerGetDataStore(schema.dimensions, seriesDimRequest.source, schema.hash);
  326. };
  327. SourceManager.prototype._innerGetDataStore = function (storeDims, seriesSource, sourceReadKey) {
  328. // TODO Can use other sourceIndex?
  329. var sourceIndex = 0;
  330. var storeList = this._storeList;
  331. var cachedStoreMap = storeList[sourceIndex];
  332. if (!cachedStoreMap) {
  333. cachedStoreMap = storeList[sourceIndex] = {};
  334. }
  335. var cachedStore = cachedStoreMap[sourceReadKey];
  336. if (!cachedStore) {
  337. var upSourceMgr = this._getUpstreamSourceManagers()[0];
  338. if (isSeries(this._sourceHost) && upSourceMgr) {
  339. cachedStore = upSourceMgr._innerGetDataStore(storeDims, seriesSource, sourceReadKey);
  340. } else {
  341. cachedStore = new DataStore(); // Always create store from source of series.
  342. cachedStore.initData(new DefaultDataProvider(seriesSource, storeDims.length), storeDims);
  343. }
  344. cachedStoreMap[sourceReadKey] = cachedStore;
  345. }
  346. return cachedStore;
  347. };
  348. /**
  349. * PENDING: Is it fast enough?
  350. * If no upstream, return empty array.
  351. */
  352. SourceManager.prototype._getUpstreamSourceManagers = function () {
  353. // Always get the relationship from the raw option.
  354. // Do not cache the link of the dependency graph, so that
  355. // there is no need to update them when change happens.
  356. var sourceHost = this._sourceHost;
  357. if (isSeries(sourceHost)) {
  358. var datasetModel = querySeriesUpstreamDatasetModel(sourceHost);
  359. return !datasetModel ? [] : [datasetModel.getSourceManager()];
  360. } else {
  361. return map(queryDatasetUpstreamDatasetModels(sourceHost), function (datasetModel) {
  362. return datasetModel.getSourceManager();
  363. });
  364. }
  365. };
  366. SourceManager.prototype._getSourceMetaRawOption = function () {
  367. var sourceHost = this._sourceHost;
  368. var seriesLayoutBy;
  369. var sourceHeader;
  370. var dimensions;
  371. if (isSeries(sourceHost)) {
  372. seriesLayoutBy = sourceHost.get('seriesLayoutBy', true);
  373. sourceHeader = sourceHost.get('sourceHeader', true);
  374. dimensions = sourceHost.get('dimensions', true);
  375. } // See [REQUIREMENT_MEMO], `non-root-dataset` do not support them.
  376. else if (!this._getUpstreamSourceManagers().length) {
  377. var model = sourceHost;
  378. seriesLayoutBy = model.get('seriesLayoutBy', true);
  379. sourceHeader = model.get('sourceHeader', true);
  380. dimensions = model.get('dimensions', true);
  381. }
  382. return {
  383. seriesLayoutBy: seriesLayoutBy,
  384. sourceHeader: sourceHeader,
  385. dimensions: dimensions
  386. };
  387. };
  388. return SourceManager;
  389. }();
  390. export { SourceManager }; // Call this method after `super.init` and `super.mergeOption` to
  391. // disable the transform merge, but do not disable transform clone from rawOption.
  392. export function disableTransformOptionMerge(datasetModel) {
  393. var transformOption = datasetModel.option.transform;
  394. transformOption && setAsPrimitive(datasetModel.option.transform);
  395. }
  396. function isSeries(sourceHost) {
  397. // Avoid circular dependency with Series.ts
  398. return sourceHost.mainType === 'series';
  399. }
  400. function doThrow(errMsg) {
  401. throw new Error(errMsg);
  402. }