rusackas commented on code in PR #40967:
URL: https://github.com/apache/superset/pull/40967#discussion_r3394642080


##########
superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/transformProps.ts:
##########
@@ -337,6 +342,89 @@ export default function transformProps(
       xAxisType,
     },
   );
+
+  // Dot size by metric (scatter): the size metric's series are excluded from
+  // rendering and instead provide per-point values that scale each marker's
+  // area between minMarkerSize and maxMarkerSize.
+  const sizeMetricLabel =
+    seriesType === EchartsTimeseriesSeriesType.Scatter && size
+      ? getMetricLabel(size)
+      : undefined;
+  const sizeSeriesLabel = isDefined(sizeMetricLabel)
+    ? (verboseMap[sizeMetricLabel!] ?? sizeMetricLabel)
+    : undefined;
+  const valueMetricLabels = ensureIsArray(metrics)
+    .map(getMetricLabel)
+    .map(label => verboseMap[label] ?? label);
+  // When the size metric is also a value metric, the query dedupes them into a
+  // single column, so each point's own value doubles as its size value.
+  const sizeIsValueMetric = isDefined(sizeSeriesLabel)
+    ? valueMetricLabels.includes(sizeSeriesLabel!)
+    : false;
+  const isSizeSeries = (name: string) =>
+    isDefined(sizeSeriesLabel) &&
+    !sizeIsValueMetric &&
+    (name === sizeSeriesLabel || name.startsWith(`${sizeSeriesLabel}, `));
+  const rawSeries = sizeSeriesLabel
+    ? allRawSeries.filter(entry => !isSizeSeries(String(entry.name ?? '')))
+    : allRawSeries;

Review Comment:
   Good catch on the edge case. Size-driven dots plus time comparison is a 
combination I didn't intend to support in this PR — the offset-named series 
would need their own handling. I'd rather scope that out than half-wire it 
here; can revisit if anyone actually wants both at once.



##########
superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/transformProps.ts:
##########
@@ -337,6 +342,89 @@ export default function transformProps(
       xAxisType,
     },
   );
+
+  // Dot size by metric (scatter): the size metric's series are excluded from
+  // rendering and instead provide per-point values that scale each marker's
+  // area between minMarkerSize and maxMarkerSize.
+  const sizeMetricLabel =
+    seriesType === EchartsTimeseriesSeriesType.Scatter && size
+      ? getMetricLabel(size)
+      : undefined;
+  const sizeSeriesLabel = isDefined(sizeMetricLabel)
+    ? (verboseMap[sizeMetricLabel!] ?? sizeMetricLabel)
+    : undefined;
+  const valueMetricLabels = ensureIsArray(metrics)
+    .map(getMetricLabel)
+    .map(label => verboseMap[label] ?? label);
+  // When the size metric is also a value metric, the query dedupes them into a
+  // single column, so each point's own value doubles as its size value.
+  const sizeIsValueMetric = isDefined(sizeSeriesLabel)
+    ? valueMetricLabels.includes(sizeSeriesLabel!)
+    : false;
+  const isSizeSeries = (name: string) =>
+    isDefined(sizeSeriesLabel) &&
+    !sizeIsValueMetric &&
+    (name === sizeSeriesLabel || name.startsWith(`${sizeSeriesLabel}, `));

Review Comment:
   Same root point as the architect thread above — the `__<offset>` series only 
show up when you mix a size metric with time comparison, which this PR doesn't 
aim to support. Leaving offset handling out deliberately rather than partially.



##########
superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/transformProps.ts:
##########
@@ -337,6 +342,89 @@ export default function transformProps(
       xAxisType,
     },
   );
+
+  // Dot size by metric (scatter): the size metric's series are excluded from
+  // rendering and instead provide per-point values that scale each marker's
+  // area between minMarkerSize and maxMarkerSize.
+  const sizeMetricLabel =
+    seriesType === EchartsTimeseriesSeriesType.Scatter && size
+      ? getMetricLabel(size)
+      : undefined;
+  const sizeSeriesLabel = isDefined(sizeMetricLabel)
+    ? (verboseMap[sizeMetricLabel!] ?? sizeMetricLabel)
+    : undefined;
+  const valueMetricLabels = ensureIsArray(metrics)
+    .map(getMetricLabel)
+    .map(label => verboseMap[label] ?? label);
+  // When the size metric is also a value metric, the query dedupes them into a
+  // single column, so each point's own value doubles as its size value.
+  const sizeIsValueMetric = isDefined(sizeSeriesLabel)
+    ? valueMetricLabels.includes(sizeSeriesLabel!)
+    : false;
+  const isSizeSeries = (name: string) =>
+    isDefined(sizeSeriesLabel) &&
+    !sizeIsValueMetric &&
+    (name === sizeSeriesLabel || name.startsWith(`${sizeSeriesLabel}, `));
+  const rawSeries = sizeSeriesLabel
+    ? allRawSeries.filter(entry => !isSizeSeries(String(entry.name ?? '')))
+    : allRawSeries;
+  // Maps each value series' dimension key to a lookup from primary-axis value
+  // to size value.
+  let sizeLookups: Map<string, Map<DataRecordValue, number>> | undefined;
+  let sizeExtent: [number, number] | undefined;
+  if (sizeSeriesLabel) {
+    let sizeMin = Infinity;
+    let sizeMax = -Infinity;
+    if (sizeIsValueMetric) {
+      rawSeries.forEach(entry => {
+        (entry.data as DataRecordValue[][]).forEach(datum => {
+          const sizeValue = isHorizontal ? datum[0] : datum[1];
+          if (typeof sizeValue === 'number' && Number.isFinite(sizeValue)) {
+            sizeMin = Math.min(sizeMin, sizeValue);
+            sizeMax = Math.max(sizeMax, sizeValue);
+          }
+        });
+      });
+    } else {
+      sizeLookups = new Map();
+      allRawSeries
+        .filter(entry => isSizeSeries(String(entry.name ?? '')))
+        .forEach(entry => {
+          const name = String(entry.name ?? '');
+          const dimsKey =
+            name === sizeSeriesLabel
+              ? ''
+              : name.slice(sizeSeriesLabel.length + 2);
+          const lookup = new Map<DataRecordValue, number>();
+          (entry.data as DataRecordValue[][]).forEach(datum => {
+            const axisValue = isHorizontal ? datum[1] : datum[0];
+            const sizeValue = isHorizontal ? datum[0] : datum[1];
+            if (typeof sizeValue === 'number' && Number.isFinite(sizeValue)) {
+              lookup.set(axisValue, sizeValue);
+              sizeMin = Math.min(sizeMin, sizeValue);
+              sizeMax = Math.max(sizeMax, sizeValue);
+            }
+          });
+          sizeLookups!.set(dimsKey, lookup);
+        });
+    }
+    if (sizeMin <= sizeMax) {
+      sizeExtent = [sizeMin, sizeMax];
+    }
+  }
+  // Strips the metric label off a series name, leaving the dimension key used
+  // to match a value series with its size series.
+  const getSeriesDimsKey = (name: string): string => {
+    const matchedLabel = valueMetricLabels.find(
+      label => name === label || name.startsWith(`${label}, `),
+    );
+    if (matchedLabel === undefined) {
+      return name;
+    }
+    return name === matchedLabel ? '' : name.slice(matchedLabel.length + 2);
+  };

Review Comment:
   Same time-comparison combination as the sibling threads — the dims-key 
extraction only diverges for `__<offset>` series, which need the size metric 
and time comparison both on. Out of scope here on purpose.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to