You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@echarts.apache.org by sh...@apache.org on 2022/03/23 12:38:04 UTC

[echarts] branch aggregate-transform updated: feat(transform): support multiple groupBy in aggregate

This is an automated email from the ASF dual-hosted git repository.

shenyi pushed a commit to branch aggregate-transform
in repository https://gitbox.apache.org/repos/asf/echarts.git


The following commit(s) were added to refs/heads/aggregate-transform by this push:
     new 197e3b3  feat(transform): support multiple groupBy in aggregate
197e3b3 is described below

commit 197e3b32127f0385ef60886571a9f24f9a18e6c4
Author: pissang <bm...@gmail.com>
AuthorDate: Wed Mar 23 20:37:22 2022 +0800

    feat(transform): support multiple groupBy in aggregate
---
 src/component/transform/aggregateTransform.ts | 113 +++++++++++++-------------
 1 file changed, 55 insertions(+), 58 deletions(-)

diff --git a/src/component/transform/aggregateTransform.ts b/src/component/transform/aggregateTransform.ts
index e4f070d..93e8d9f 100644
--- a/src/component/transform/aggregateTransform.ts
+++ b/src/component/transform/aggregateTransform.ts
@@ -16,7 +16,7 @@
 * specific language governing permissions and limitations
 * under the License.
 */
-import { assert, each, keys, map, retrieve2 } from 'zrender/src/core/util';
+import { assert, each, find, isArray, keys, map, retrieve2 } from 'zrender/src/core/util';
 import {
     DataTransformOption,
     ExternalDataTransform,
@@ -27,6 +27,7 @@ import { asc, quantile } from '../../util/number';
 import { DimensionLoose, DimensionName, OptionDataValue } from '../../util/types';
 
 
+const GROUP_KEY_SEPARATER = '$EC$';
 /**
  * @usage
  *
@@ -81,7 +82,7 @@ export interface AggregateTransformOption extends DataTransformOption {
             method: AggregateMethodLoose;
         }[];
         // Optional
-        groupBy: DimensionLoose;
+        groupBy: DimensionLoose | DimensionLoose[];
     };
 }
 
@@ -108,7 +109,7 @@ class AggregateResult {
      */
     readonly dep: AggregateResult;
 
-    readonly groupBy: ExternalDimensionDefinition;
+    readonly groupBy: ExternalDimensionDefinition[];
 
 
     values: AggregateResultValue | AggregateResultValueByGroup;
@@ -118,7 +119,7 @@ class AggregateResult {
         indexInUpstream: number,
         method: AggregateMethodInternal,
         name: DimensionName,
-        groupBy: ExternalDimensionDefinition,
+        groupBy: ExternalDimensionDefinition[],
         dep?: AggregateResult
     ) {
         this.method = method;
@@ -145,13 +146,6 @@ class AggregateResult {
 
     set: (groupByVal: OptionDataValue, value: AggregateResultValue) => void;
     get: (groupByVal: OptionDataValue) => AggregateResultValue;
-
-    // getValues(): AggregateResultValue[] {
-    //     const values = this._values;
-    //     return this.groupBy
-    //         ? map(keys(values as AggregateResultValueByGroup), key => (values as AggregateResultValueByGroup)[key])
-    //         : [values as AggregateResultValue];
-    // }
 }
 
 export const aggregateTransform: ExternalDataTransform<AggregateTransformOption> = {
@@ -162,15 +156,15 @@ export const aggregateTransform: ExternalDataTransform<AggregateTransformOption>
         const upstream = params.upstream;
         const config = params.config;
 
-        const { aggResults, groupByDim } = prepare(config, upstream);
+        const { aggResults, groupByDims } = prepare(config, upstream);
 
         // Calculate
-        doAggregate(groupByDim, upstream, aggResults);
+        doAggregate(groupByDims, upstream, aggResults);
 
         // Convert to output row format.
         let data: OptionDataValue[][];
 
-        if (groupByDim && aggResults.length) {
+        if (groupByDims && aggResults.length) {
             const groupKeys = keys(aggResults[0].values as any);
             data = map(groupKeys, key => []);
 
@@ -197,16 +191,19 @@ function prepare(
     upstream: ExternalSource
 ): {
     aggResults: AggregateResult[];
-    groupByDim: ExternalDimensionDefinition
+    groupByDims?: ExternalDimensionDefinition[]
 } {
     const outputConfig = config.output;
     const aggResults: AggregateResult[] = [];
 
-    const groupByConfig = config.groupBy;
-    let groupByDim: ExternalDimensionDefinition;
+    let groupByConfig = config.groupBy;
+    let groupByDims: ExternalDimensionDefinition[];
     if (groupByConfig != null) {
-        groupByDim = upstream.getDimensionInfo(groupByConfig);
-        assert(groupByDim, 'Can not find dimension by `groupBy`: ' + groupByConfig);
+        if (!isArray(groupByConfig)) {
+            groupByConfig = [groupByConfig];
+        }
+        groupByDims = map(groupByConfig, g => upstream.getDimensionInfo(g));
+        assert(groupByDims, 'Can not find dimension by `groupBy`: ' + groupByConfig);
     }
 
     each(outputConfig, resultDimInfoConfig => {
@@ -214,10 +211,12 @@ function prepare(
         const dimInfoInUpstream = upstream.getDimensionInfo(resultDimInfoConfig.from);
         if (__DEV__) {
             assert(dimInfoInUpstream, 'Can not find dimension by `from`: ' + resultDimInfoConfig.from);
-            assert(
-                groupByDim.index !== dimInfoInUpstream.index || resultDimInfoConfig.method == null,
-                `Dimension ${dimInfoInUpstream.name} is the "groupBy" dimension, must not have any "method".`
-            );
+            each(groupByDims, (gbDim) => {
+                assert(
+                    gbDim.index !== dimInfoInUpstream.index || resultDimInfoConfig.method == null,
+                    `Dimension ${dimInfoInUpstream.name} is the "groupBy" dimension, must not have any "method".`
+                );
+            });
         }
 
         const methodName = (resultDimInfoConfig.method || '').toUpperCase() as AggregateMethodInternal
@@ -235,39 +234,31 @@ function prepare(
             indexInUpStream,
             methodName,
             name,
-            groupByDim,
+            groupByDims,
             method.dep && new AggregateResult(
-                -1, indexInUpStream, method.dep, name, groupByDim
+                -1, indexInUpStream, method.dep, name, groupByDims
             )
         );
         aggResults.push(finalResultDimInfo);
     });
 
-    return { aggResults, groupByDim };
+    return { aggResults, groupByDims };
 }
 
 function doAggregate(
-    groupByDim: ExternalDimensionDefinition,
+    groupByDims: ExternalDimensionDefinition[] | undefined,
     upstream: ExternalSource,
     aggResultDims: AggregateResult[]
 ) {
 
-    function doCreate(aggResult: AggregateResult, dataIndex: number, groupByVal?: string) {
-        const method = methods[aggResult.method];
-        const val = upstream.retrieveValue(dataIndex, aggResult.fromIndex);
-        aggResult.set(
-            groupByVal,
-            isGroupByDimension(groupByDim, aggResult)
-                ? groupByVal
-                : method.init(val, aggResult, groupByVal)
+    function doCreate(isGroupByDim: boolean, aggResult: AggregateResult, val: OptionDataValue, groupByVal?: string) {
+        aggResult.set(groupByVal, isGroupByDim
+            ? groupByVal
+            : methods[aggResult.method].init(val, aggResult, groupByVal)
         );
     };
-    function doUpdate(aggResult: AggregateResult, dataIndex: number, groupByVal?: string) {
-        if (isGroupByDimension(groupByDim, aggResult)) {
-            return;
-        }
+    function doUpdate(aggResult: AggregateResult, val: OptionDataValue, groupByVal?: string) {
         const method = methods[aggResult.method];
-        const val = upstream.retrieveValue(dataIndex, aggResult.fromIndex);
         if (method.add) {
             aggResult.set(
                 groupByVal,
@@ -281,34 +272,40 @@ function doAggregate(
 
         // TODO share dep result
         if (aggResult.dep) {
-            doAggregate(groupByDim, upstream, [aggResult.dep]);
+            doAggregate(groupByDims, upstream, [aggResult.dep]);
         }
 
-        if (groupByDim) {
+        if (groupByDims) {
+            const isGroupByDim = isGroupByDimension(groupByDims, aggResult);
             const groupCreated: Record<string, boolean> = {};
-
-            for (let dataIndex = 0, len = upstream.count(); dataIndex < len; dataIndex++) {
-                let groupByVal = upstream.retrieveValue(dataIndex, groupByDim.index) as string;
-
-                // PENDING: when value is null/undefined
-                if (groupByVal == null) {
-                    continue;
+            const keyArr: string[] = [];
+            outer: for (let dataIndex = 0, len = upstream.count(); dataIndex < len; dataIndex++) {
+                for (let i = 0; i < groupByDims.length; i++) {
+                    keyArr[i] = upstream.retrieveValue(dataIndex, groupByDims[i].index) as string;
+                    if (keyArr[i] == null) {
+                        // PENDING: when value is null/undefined
+                        continue outer;
+                    }
                 }
-                groupByVal += '';
+                // TODO key conflicts?
+                const groupByVal = keyArr.join(GROUP_KEY_SEPARATER);
+                const val = upstream.retrieveValue(dataIndex, aggResult.fromIndex);
 
                 if (!groupCreated[groupByVal]) {
-                    doCreate(aggResult, dataIndex, groupByVal);
+                    doCreate(isGroupByDim, aggResult, val, groupByVal);
                     groupCreated[groupByVal + ''] = true;
                 }
-                else {
-                    doUpdate(aggResult, dataIndex, groupByVal);
+                else if (!isGroupByDim) {
+                    doUpdate(aggResult, val, groupByVal);
                 }
             }
         }
         else {
-            doCreate(aggResult, 0);
-            for (let dataIndex = 1, len = upstream.count(); dataIndex < len; dataIndex++) {
-                doUpdate(aggResult, dataIndex);
+            for (let dataIndex = 0, len = upstream.count(); dataIndex < len; dataIndex++) {
+                const val = upstream.retrieveValue(dataIndex, aggResult.fromIndex);
+                dataIndex
+                    ? doUpdate(aggResult, val)
+                    : doCreate(false, aggResult, val);
             }
         }
     }
@@ -316,10 +313,10 @@ function doAggregate(
 
 
 function isGroupByDimension(
-    groupByDim: ExternalDimensionDefinition,
+    groupByDims: ExternalDimensionDefinition[],
     targetDimInfo: AggregateResult
 ): boolean {
-    return groupByDim && targetDimInfo.fromIndex === groupByDim.index;
+    return !!find(groupByDims, dim => dim.index === targetDimInfo.fromIndex);
 }
 
 type MethodInit = (

---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@echarts.apache.org
For additional commands, e-mail: commits-help@echarts.apache.org