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

vogievetsky pushed a commit to branch segment_timeline2
in repository https://gitbox.apache.org/repos/asf/druid.git

commit d80b2beb9566b4ef94ed1f9ab49051cda48433b1
Author: Vadim Ogievetsky <[email protected]>
AuthorDate: Mon Nov 4 12:38:13 2024 -0800

    preogress
---
 .../src/components/segment-timeline/bubble.tsx     |  9 ++-
 .../segment-timeline/segment-bar-chart-render.tsx  | 64 +++++++++++-------
 .../segment-timeline/segment-bar-chart.tsx         | 77 +++++++++++-----------
 .../segment-timeline/segment-timeline.tsx          |  9 +--
 4 files changed, 89 insertions(+), 70 deletions(-)

diff --git a/web-console/src/components/segment-timeline/bubble.tsx 
b/web-console/src/components/segment-timeline/bubble.tsx
index f3a9c287b13..efb72431379 100644
--- a/web-console/src/components/segment-timeline/bubble.tsx
+++ b/web-console/src/components/segment-timeline/bubble.tsx
@@ -17,22 +17,25 @@
  */
 
 import classNames from 'classnames';
+import type { ReactNode } from 'react';
 import { createPortal } from 'react-dom';
 
 import './bubble.scss';
 
 interface BubbleProps {
-  openOn: { x: number; y: number; text: string };
+  className?: string;
+  openOn: { x: number; y: number; text: ReactNode } | undefined;
   direction?: 'up' | 'down';
   mute?: boolean;
 }
 
 export const Bubble = function Bubble(props: BubbleProps) {
-  const { openOn, direction = 'up', mute } = props;
+  const { className, openOn, direction = 'up', mute } = props;
+  if (!openOn) return null;
 
   return createPortal(
     <div
-      className={classNames('bubble', direction, { mute })}
+      className={classNames('bubble', className, direction, { mute })}
       style={{ left: openOn.x, top: openOn.y }}
     >
       {openOn.text}
diff --git 
a/web-console/src/components/segment-timeline/segment-bar-chart-render.tsx 
b/web-console/src/components/segment-timeline/segment-bar-chart-render.tsx
index edb3a90e655..0152188e8e2 100644
--- a/web-console/src/components/segment-timeline/segment-bar-chart-render.tsx
+++ b/web-console/src/components/segment-timeline/segment-bar-chart-render.tsx
@@ -22,7 +22,7 @@ import classNames from 'classnames';
 import { max } from 'd3-array';
 import { axisBottom, axisLeft } from 'd3-axis';
 import { scaleLinear, scaleUtc } from 'd3-scale';
-import type React from 'react';
+import type { ReactNode } from 'react';
 import { useMemo, useRef, useState } from 'react';
 
 import { getDatasourceColor } from '../../druid-models';
@@ -50,7 +50,7 @@ import { aggregateSegmentStats } from './common';
 
 import './segment-bar-chart-render.scss';
 
-const CHART_MARGIN: Margin = { top: 40, right: 0, bottom: 25, left: 70 };
+const CHART_MARGIN: Margin = { top: 10, right: 0, bottom: 25, left: 70 };
 const MIN_BAR_WIDTH = 2;
 const POSSIBLE_GRANULARITIES = [
   new Duration('PT15M'),
@@ -117,7 +117,7 @@ export const SegmentBarChartRender = function 
SegmentBarChartRender(
     focusDatasource,
     changeFocusDatasource,
   } = props;
-  const [hoverOn, setHoverOn] = useState<IntervalBar>();
+  const [hoveredIntervalBar, setHoveredIntervalBar] = useState<IntervalBar>();
   const [mouseDownAt, setMouseDownAt] = useState<
     { time: Date; action: 'select' | 'shift' } | undefined
   >();
@@ -223,7 +223,7 @@ export const SegmentBarChartRender = function 
SegmentBarChartRender(
 
   function handleMouseDown(e: React.MouseEvent) {
     const svg = svgRef.current;
-    if (!svg) return;
+    if (!svg || hoveredIntervalBar) return;
     e.preventDefault();
     const rect = svg.getBoundingClientRect();
     const x = e.clientX - rect.x - CHART_MARGIN.left;
@@ -327,6 +327,33 @@ export const SegmentBarChartRender = function 
SegmentBarChartRender(
 
   console.log('Bar chart render');
 
+  let hoveredOpenOn: { x: number; y: number; text: ReactNode } | undefined;
+  if (hoveredIntervalBar && svgRef.current) {
+    const rect = svgRef.current.getBoundingClientRect();
+    hoveredOpenOn = {
+      x:
+        rect.x +
+        CHART_MARGIN.left +
+        timeScale(
+          new Date((hoveredIntervalBar.start.valueOf() + 
hoveredIntervalBar.end.valueOf()) / 2),
+        ),
+      y: rect.y + CHART_MARGIN.top - 10,
+      text: (
+        <>
+          <div>Datasource: {hoveredIntervalBar.datasource}</div>
+          <div>{`Time: ${prettyFormatIsoDate(hoveredIntervalBar.start)}/${
+            hoveredIntervalBar.originalTimeSpan
+          }`}</div>
+          <div>
+            {`${capitalizeFirst(shownIntervalStat)}: ${formatTick(
+              hoveredIntervalBar[shownIntervalStat],
+            )}`}
+          </div>
+        </>
+      ),
+    };
+  }
+
   return (
     <div className="segment-bar-chart-render">
       <svg
@@ -339,7 +366,7 @@ export const SegmentBarChartRender = function 
SegmentBarChartRender(
       >
         <g
           transform={`translate(${CHART_MARGIN.left},${CHART_MARGIN.top})`}
-          onMouseLeave={() => setHoverOn(undefined)}
+          onMouseLeave={() => setHoveredIntervalBar(undefined)}
         >
           <ChartAxis
             className="gridline-x"
@@ -389,18 +416,18 @@ export const SegmentBarChartRender = function 
SegmentBarChartRender(
                   onClick={() => changeFocusDatasource(intervalBar.datasource)}
                   onMouseOver={() => {
                     if (mouseDownAt) return;
-                    setHoverOn(intervalBar);
+                    setHoveredIntervalBar(intervalBar);
                   }}
                 />
               );
             })}
-            {hoverOn && (
+            {hoveredIntervalBar && (
               <rect
                 className="hovered-bar"
-                {...segmentBarToRect(hoverOn)}
+                {...segmentBarToRect(hoveredIntervalBar)}
                 onClick={() => {
-                  setHoverOn(undefined);
-                  changeFocusDatasource(hoverOn.datasource);
+                  setHoveredIntervalBar(undefined);
+                  changeFocusDatasource(hoveredIntervalBar.datasource);
                 }}
               />
             )}
@@ -429,26 +456,13 @@ export const SegmentBarChartRender = function 
SegmentBarChartRender(
           </g>
         </g>
       </svg>
-      {dragging ? (
-        <div className="bar-chart-tooltip">
-          <div>Start: {dragging[0].toISOString()}</div>
-          <div>End: {dragging[1].toISOString()}</div>
-        </div>
-      ) : hoverOn ? (
-        <div className="bar-chart-tooltip">
-          <div>Datasource: {hoverOn.datasource}</div>
-          <div>{`Time: 
${prettyFormatIsoDate(hoverOn.start)}/${hoverOn.originalTimeSpan}`}</div>
-          <div>
-            {`${capitalizeFirst(shownIntervalStat)}: 
${formatTick(hoverOn[shownIntervalStat])}`}
-          </div>
-        </div>
-      ) : undefined}
       {!intervalRows.length && (
         <div className="empty-placeholder">
           <div className="no-data-text">There are no segments in the selected 
range</div>
         </div>
       )}
-      {bubbleOpenOn && <Bubble openOn={bubbleOpenOn} mute direction="down" />}
+      <Bubble openOn={hoveredOpenOn} mute direction="up" />
+      <Bubble openOn={bubbleOpenOn} mute direction="down" />
     </div>
   );
 };
diff --git a/web-console/src/components/segment-timeline/segment-bar-chart.tsx 
b/web-console/src/components/segment-timeline/segment-bar-chart.tsx
index bcbe645c58e..047fda7fdd2 100644
--- a/web-console/src/components/segment-timeline/segment-bar-chart.tsx
+++ b/web-console/src/components/segment-timeline/segment-bar-chart.tsx
@@ -17,9 +17,10 @@
  */
 
 import type { NonNullDateRange } from '@blueprintjs/datetime';
-import { C, F, N, sql, SqlQuery } from '@druid-toolkit/query';
+import { C, F, L, N, sql, SqlExpression, SqlQuery } from 
'@druid-toolkit/query';
 import { useMemo } from 'react';
 
+import { END_OF_TIME_DATE, START_OF_TIME_DATE } from '../../druid-models';
 import type { Capabilities } from '../../helpers';
 import { useQueryManager } from '../../hooks';
 import { Api } from '../../singletons';
@@ -53,15 +54,25 @@ export const SegmentBarChart = function 
SegmentBarChart(props: SegmentBarChartPr
     changeFocusDatasource,
   } = props;
 
-  const intervalsQuery = useMemo(() => ({ capabilities, dateRange }), 
[capabilities, dateRange]);
+  const intervalsQuery = useMemo(
+    () => ({ capabilities, dateRange, focusDatasource }),
+    [capabilities, dateRange, focusDatasource],
+  );
 
   const [intervalRowsState] = useQueryManager({
     query: intervalsQuery,
-    processQuery: async ({ capabilities, dateRange }, cancelToken) => {
+    processQuery: async ({ capabilities, dateRange, focusDatasource }, 
cancelToken) => {
       if (capabilities.hasSql()) {
         const query = SqlQuery.from(N('sys').table('segments'))
           .changeWhereExpression(
-            sql`'${dateRange[0].toISOString()}' <= "end" AND "start" <= 
'${dateRange[1].toISOString()}' AND is_published = 1 AND is_overshadowed = 0`,
+            SqlExpression.and(
+              sql`'${dateRange[0].toISOString()}' <= "end" AND "start" <= 
'${dateRange[1].toISOString()}'`,
+              C('start').unequal(START_OF_TIME_DATE),
+              C('end').unequal(END_OF_TIME_DATE),
+              C('is_published').equal(1),
+              C('is_overshadowed').equal(0),
+              focusDatasource ? C('datasource').equal(L(focusDatasource)) : 
undefined,
+            ),
           )
           .addSelect(C('start'), { addToGroupBy: 'end' })
           .addSelect(C('end'), { addToGroupBy: 'end' })
@@ -82,41 +93,31 @@ export const SegmentBarChart = function 
SegmentBarChart(props: SegmentBarChartPr
           };
         }); // This trimming should ideally be pushed into the SQL query but 
at the time of this writing queries on the sys.* tables do not allow substring
       } else {
-        const datasources = await getApiArray<string>(
-          `/druid/coordinator/v1/datasources`,
-          cancelToken,
+        return filterMap(
+          await getApiArray(
+            `/druid/coordinator/v1/metadata/segments?${
+              focusDatasource ? 
`datasources=${Api.encodePath(focusDatasource)}` : ''
+            }`,
+            cancelToken,
+          ),
+          (segment: any) => {
+            const [startStr, endStr] = segment.interval.split('/');
+            if (startStr === START_OF_TIME_DATE && endStr === 
END_OF_TIME_DATE) return;
+            const start = new Date(startStr);
+            const end = new Date(endStr);
+            if (!(dateRange[0] <= end && start <= dateRange[1])) return;
+
+            return {
+              start,
+              end,
+              datasource: segment.dataSource,
+              originalTimeSpan: Duration.fromRange(start, end, TZ_UTC),
+              segments: 1,
+              size: segment.size,
+              num_rows: segment.num_rows || 0, // segment.num_rows is really 
null on this API :-(
+            };
+          },
         );
-
-        return (
-          await Promise.all(
-            datasources.map(async datasource => {
-              const intervalMap = (
-                await Api.instance.get(
-                  `/druid/coordinator/v1/datasources/${Api.encodePath(
-                    datasource,
-                  )}/intervals?simple`,
-                  { cancelToken },
-                )
-              ).data;
-
-              return filterMap(Object.entries(intervalMap), ([interval, v]) => 
{
-                const [startStr, endStr] = interval.split('/');
-                const start = new Date(startStr);
-                const end = new Date(endStr);
-                // ToDo: Filter on start end
-                const { count, size, rows } = v as any;
-                return {
-                  start,
-                  end,
-                  datasource,
-                  segments: count,
-                  size,
-                  rows,
-                };
-              });
-            }),
-          )
-        ).flat();
       }
     },
   });
diff --git a/web-console/src/components/segment-timeline/segment-timeline.tsx 
b/web-console/src/components/segment-timeline/segment-timeline.tsx
index 2a683b011ea..92761368d53 100644
--- a/web-console/src/components/segment-timeline/segment-timeline.tsx
+++ b/web-console/src/components/segment-timeline/segment-timeline.tsx
@@ -67,6 +67,8 @@ const SHOWN_DURATION_OPTIONS: Duration[] = [
   new Duration('P10Y'),
 ];
 
+const SHOW_ALL = 'Show all';
+
 function getDateRange(shownDuration: Duration): NonNullDateRange {
   const end = day.ceil(new Date(), TZ_UTC);
   return [shownDuration.shift(end, TZ_UTC, -1), end];
@@ -101,13 +103,12 @@ export const SegmentTimeline = function 
SegmentTimeline(props: SegmentTimelinePr
   });
 
   const DatasourceSelect: React.FC = () => {
-    const showAll = 'Show all';
-    const datasourcesWzAll = [showAll].concat(datasourcesState.data || []);
+    const datasourcesWzAll = [SHOW_ALL].concat(datasourcesState.data || []);
     return (
       <Select<string>
         items={datasourcesWzAll}
         onItemSelect={(selectedItem: string) => {
-          setFocusDatasource(selectedItem === showAll ? undefined : 
selectedItem);
+          setFocusDatasource(selectedItem === SHOW_ALL ? undefined : 
selectedItem);
         }}
         itemRenderer={(val, { handleClick, handleFocus, modifiers }) => {
           if (!modifiers.matchesPredicate) return null;
@@ -135,7 +136,7 @@ export const SegmentTimeline = function 
SegmentTimeline(props: SegmentTimelinePr
           }
         }}
       >
-        <Button text={focusDatasource ?? showAll} 
rightIcon={IconNames.CARET_DOWN} />
+        <Button text={focusDatasource ?? SHOW_ALL} 
rightIcon={IconNames.CARET_DOWN} />
       </Select>
     );
   };


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

Reply via email to