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 02b963d818885b60380e2c01021142088a6bef89
Author: Vadim Ogievetsky <[email protected]>
AuthorDate: Wed Nov 6 20:43:24 2024 -0800

    fixes
---
 .../segment-timeline/segment-bar-chart-render.tsx  |   3 +-
 .../segment-timeline/segment-bar-chart.tsx         |   4 +-
 .../segment-timeline/segment-timeline.spec.tsx     |   2 +-
 .../segment-timeline/segment-timeline.tsx          | 141 ++++++++++++++++-----
 .../views/datasources-view/datasources-view.tsx    |   9 +-
 .../src/views/segments-view/segments-view.tsx      |   2 +-
 6 files changed, 117 insertions(+), 44 deletions(-)

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 054e06a1725..4666656e246 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
@@ -213,7 +213,6 @@ export const SegmentBarChartRender = function 
SegmentBarChartRender(
   const baseTimeScale = scaleUtc()
     .domain(dateRange)
     .range([EXTEND_X_SCALE_DOMAIN_BY, innerStage.width - 
EXTEND_X_SCALE_DOMAIN_BY]);
-
   const timeScale = shiftOffset
     ? baseTimeScale.copy().domain(offsetDateRange(dateRange, shiftOffset))
     : baseTimeScale;
@@ -224,7 +223,7 @@ export const SegmentBarChartRender = function 
SegmentBarChartRender(
   );
   const statScale = scaleLinear()
     .rangeRound([innerStage.height, 0])
-    .domain([0, maxNormalizedStat ?? 1]);
+    .domain([0, (maxNormalizedStat ?? 1) * 1.05]);
 
   const formatTickRate = (n: number) => {
     switch (shownIntervalStat) {
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 b745014d0a2..87fb41bc278 100644
--- a/web-console/src/components/segment-timeline/segment-bar-chart.tsx
+++ b/web-console/src/components/segment-timeline/segment-bar-chart.tsx
@@ -83,8 +83,6 @@ export const SegmentBarChart = function 
SegmentBarChart(props: SegmentBarChartPr
           .addSelect(F.sum(C('num_rows')).as('rows'))
           .toString();
 
-        console.log(query);
-
         return (await queryDruidSql({ query }, cancelToken)).map(sr => {
           const start = new Date(sr.start);
           const end = new Date(sr.end);
@@ -96,7 +94,7 @@ export const SegmentBarChart = function 
SegmentBarChart(props: SegmentBarChartPr
             realtime: Boolean(sr.realtime),
             originalTimeSpan: Duration.fromRange(start, end, TZ_UTC),
           } as IntervalRow;
-        }); // 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 {
         return filterMap(
           await getApiArray(
diff --git 
a/web-console/src/components/segment-timeline/segment-timeline.spec.tsx 
b/web-console/src/components/segment-timeline/segment-timeline.spec.tsx
index 966e3113153..dbbb7c35fe3 100644
--- a/web-console/src/components/segment-timeline/segment-timeline.spec.tsx
+++ b/web-console/src/components/segment-timeline/segment-timeline.spec.tsx
@@ -25,7 +25,7 @@ import { SegmentTimeline } from './segment-timeline';
 describe('SegmentTimeline', () => {
   it('matches snapshot', () => {
     const segmentTimeline = (
-      <SegmentTimeline capabilities={Capabilities.FULL} 
initDatasource={undefined} />
+      <SegmentTimeline capabilities={Capabilities.FULL} datasource={undefined} 
/>
     );
     const { container } = render(segmentTimeline);
     expect(container.firstChild).toMatchSnapshot();
diff --git a/web-console/src/components/segment-timeline/segment-timeline.tsx 
b/web-console/src/components/segment-timeline/segment-timeline.tsx
index 75964a70205..4446d7fff99 100644
--- a/web-console/src/components/segment-timeline/segment-timeline.tsx
+++ b/web-console/src/components/segment-timeline/segment-timeline.tsx
@@ -29,8 +29,10 @@ import type { NonNullDateRange } from 
'@blueprintjs/datetime';
 import { DateRangePicker3 } from '@blueprintjs/datetime2';
 import { IconNames } from '@blueprintjs/icons';
 import { Select } from '@blueprintjs/select';
-import { useState } from 'react';
+import { C, L, N, SqlExpression, SqlQuery } from '@druid-toolkit/query';
+import { useEffect, useMemo, useState } from 'react';
 
+import { END_OF_TIME_DATE, START_OF_TIME_DATE } from '../../druid-models';
 import type { Capabilities } from '../../helpers';
 import { useQueryManager } from '../../hooks';
 import {
@@ -40,12 +42,12 @@ import {
   getApiArray,
   isNonNullRange,
   localToUtcDateRange,
-  prettyFormatIsoDate,
   queryDruidSql,
   TZ_UTC,
   utcToLocalDateRange,
 } from '../../utils';
 import { Stage } from '../../utils/stage';
+import { Loader } from '../loader/loader';
 
 import type { IntervalStat } from './common';
 import { getIntervalStatTitle, INTERVAL_STATS } from './common';
@@ -55,7 +57,7 @@ import './segment-timeline.scss';
 
 interface SegmentTimelineProps {
   capabilities: Capabilities;
-  initDatasource: string | undefined;
+  datasource: string | undefined;
 }
 
 const DEFAULT_SHOWN_DURATION = new Duration('P3M');
@@ -74,16 +76,30 @@ function getDateRange(shownDuration: Duration): 
NonNullDateRange {
   return [shownDuration.shift(end, TZ_UTC, -1), end];
 }
 
+function formatDateRange(dateRange: NonNullDateRange): string {
+  return `${dateRange[0].toISOString().slice(0, 10)} → 
${dateRange[1].toISOString().slice(0, 10)}`;
+}
+
+function dateRangesEqual(dr1: NonNullDateRange, dr2: NonNullDateRange): 
boolean {
+  return dr1[0].valueOf() === dr2[0].valueOf() && dr2[1].valueOf() === 
dr2[1].valueOf();
+}
+
 export const SegmentTimeline = function SegmentTimeline(props: 
SegmentTimelineProps) {
-  const { capabilities, initDatasource } = props;
+  const { capabilities, datasource } = props;
   const [stage, setStage] = useState<Stage | undefined>();
   const [activeSegmentStat, setActiveSegmentStat] = 
useState<IntervalStat>('size');
-  const [shownDatasource, setShownDatasource] = useState<string | 
undefined>(initDatasource);
-  const [dateRange, setDateRange] = useState<NonNullDateRange>(
-    getDateRange(DEFAULT_SHOWN_DURATION),
-  );
+  const [shownDatasource, setShownDatasource] = useState<string | 
undefined>(datasource);
+  const [dateRange, setDateRange] = useState<NonNullDateRange | undefined>();
   const [showCustomDatePicker, setShowCustomDatePicker] = useState(false);
 
+  useEffect(() => {
+    setShownDatasource(datasource);
+  }, [datasource]);
+
+  const defaultDateRange = useMemo(() => {
+    return getDateRange(DEFAULT_SHOWN_DURATION);
+  }, []);
+
   const [datasourcesState] = useQueryManager<Capabilities, string[]>({
     initQuery: capabilities,
     processQuery: async (capabilities, cancelToken) => {
@@ -102,6 +118,55 @@ export const SegmentTimeline = function 
SegmentTimeline(props: SegmentTimelinePr
     },
   });
 
+  const [initDatasourceDateRangeState] = useQueryManager<string, 
NonNullDateRange>({
+    query: dateRange ? undefined : shownDatasource,
+    processQuery: async (datasource, cancelToken) => {
+      if (capabilities.hasSql()) {
+        const baseQuery = SqlQuery.from(N('sys').table('segments'))
+          .changeWhereExpression(
+            SqlExpression.and(
+              C('start').unequal(START_OF_TIME_DATE),
+              C('end').unequal(END_OF_TIME_DATE),
+              C('is_overshadowed').equal(0),
+              C('datasource').equal(L(datasource)),
+            ),
+          )
+          .changeLimitValue(1);
+
+        const startQuery = baseQuery
+          .addSelect(C('start'), { addToOrderBy: 'end', direction: 'ASC' })
+          .toString();
+
+        const startRes = await queryDruidSql<{ start: string }>({ query: 
startQuery }, cancelToken);
+        if (startRes.length !== 1) {
+          return getDateRange(DEFAULT_SHOWN_DURATION);
+        }
+
+        const start = day.floor(new Date(startRes[0].start), TZ_UTC);
+
+        const endQuery = baseQuery
+          .addSelect(C('end'), { addToOrderBy: 'end', direction: 'DESC' })
+          .toString();
+
+        const endRes = await queryDruidSql<{ end: string }>({ query: endQuery 
}, cancelToken);
+        if (endRes.length !== 1) {
+          return [start, day.ceil(new Date(), TZ_UTC)]; // Should not really 
get here
+        }
+
+        const end = day.ceil(new Date(endRes[0].end), TZ_UTC);
+        return [start, end];
+      } else {
+        // ToDo: fill me
+        throw new Error('fill me');
+      }
+    },
+  });
+
+  const effectiveDateRange =
+    dateRange ||
+    initDatasourceDateRangeState.data ||
+    (initDatasourceDateRangeState.isLoading() ? undefined : defaultDateRange);
+
   return (
     <div className="segment-timeline">
       <div className="control-bar">
@@ -110,20 +175,24 @@ export const SegmentTimeline = function 
SegmentTimeline(props: SegmentTimelinePr
             icon={IconNames.CARET_LEFT}
             data-tooltip="Previous time period"
             small
+            disabled={!effectiveDateRange}
             onClick={() => {
-              const d = Duration.fromRange(dateRange[0], dateRange[1], TZ_UTC);
-              setDateRange([d.shift(dateRange[0], TZ_UTC, -1), dateRange[0]]);
+              if (!effectiveDateRange) return;
+              const d = Duration.fromRange(effectiveDateRange[0], 
effectiveDateRange[1], TZ_UTC);
+              setDateRange([d.shift(effectiveDateRange[0], TZ_UTC, -1), 
effectiveDateRange[0]]);
             }}
           />
           <Button
             icon={IconNames.ZOOM_OUT}
             data-tooltip="Zoom out"
             small
+            disabled={!effectiveDateRange}
             onClick={e => {
-              const d = Duration.fromRange(dateRange[0], dateRange[1], TZ_UTC);
+              if (!effectiveDateRange) return;
+              const d = Duration.fromRange(effectiveDateRange[0], 
effectiveDateRange[1], TZ_UTC);
               setDateRange([
-                d.shift(dateRange[0], TZ_UTC, -1),
-                e.altKey ? d.shift(dateRange[1], TZ_UTC, 1) : dateRange[1],
+                d.shift(effectiveDateRange[0], TZ_UTC, -1),
+                e.altKey ? d.shift(effectiveDateRange[1], TZ_UTC, 1) : 
effectiveDateRange[1],
               ]);
             }}
           />
@@ -131,28 +200,36 @@ export const SegmentTimeline = function 
SegmentTimeline(props: SegmentTimelinePr
             icon={IconNames.CARET_RIGHT}
             data-tooltip="Next time period"
             small
+            disabled={!effectiveDateRange}
             onClick={() => {
-              const d = Duration.fromRange(dateRange[0], dateRange[1], TZ_UTC);
-              setDateRange([dateRange[1], d.shift(dateRange[1], TZ_UTC, 1)]);
+              if (!effectiveDateRange) return;
+              const d = Duration.fromRange(effectiveDateRange[0], 
effectiveDateRange[1], TZ_UTC);
+              setDateRange([effectiveDateRange[1], 
d.shift(effectiveDateRange[1], TZ_UTC, 1)]);
             }}
           />
         </ButtonGroup>
         <ButtonGroup>
-          {SHOWN_DURATION_OPTIONS.map((d, i) => (
-            <Button
-              key={i}
-              text={d.toString().replace('P', '')}
-              data-tooltip={`Show last ${d.getDescription()}`}
-              small
-              onClick={() => setDateRange(getDateRange(d))}
-            />
-          ))}
+          {SHOWN_DURATION_OPTIONS.map((d, i) => {
+            const dr = getDateRange(d);
+            return (
+              <Button
+                key={i}
+                text={d.toString().replace('P', '')}
+                active={effectiveDateRange && 
dateRangesEqual(effectiveDateRange, dr)}
+                data-tooltip={`Show last 
${d.getDescription()}\n${formatDateRange(dr)}`}
+                small
+                onClick={() => setDateRange(dr)}
+              />
+            );
+          })}
           <Popover
             isOpen={showCustomDatePicker}
             onInteraction={setShowCustomDatePicker}
             content={
               <DateRangePicker3
-                defaultValue={utcToLocalDateRange(dateRange)}
+                defaultValue={utcToLocalDateRange(
+                  effectiveDateRange || getDateRange(DEFAULT_SHOWN_DURATION),
+                )}
                 onChange={newDateRange => {
                   const newUtcDateRange = localToUtcDateRange(newDateRange);
                   if (!isNonNullRange(newUtcDateRange)) return;
@@ -168,14 +245,9 @@ export const SegmentTimeline = function 
SegmentTimeline(props: SegmentTimelinePr
           >
             <Button
               icon={IconNames.CALENDAR}
+              text={effectiveDateRange ? formatDateRange(effectiveDateRange) : 
'? → ?'}
               small
-              data-tooltip={
-                showCustomDatePicker
-                  ? undefined
-                  : `Select a custom date range\nCurrent range: 
${prettyFormatIsoDate(
-                      dateRange[0],
-                    )} - ${prettyFormatIsoDate(dateRange[1])}`
-              }
+              data-tooltip={showCustomDatePicker ? undefined : `Select a 
custom date range`}
             />
           </Popover>
         </ButtonGroup>
@@ -248,11 +320,11 @@ export const SegmentTimeline = function 
SegmentTimeline(props: SegmentTimelinePr
         }}
       >
         <div className="chart-container">
-          {stage && (
+          {stage && effectiveDateRange && (
             <SegmentBarChart
               capabilities={capabilities}
               stage={stage}
-              dateRange={dateRange}
+              dateRange={effectiveDateRange}
               changeDateRange={setDateRange}
               shownIntervalStat={activeSegmentStat}
               shownDatasource={shownDatasource}
@@ -261,6 +333,7 @@ export const SegmentTimeline = function 
SegmentTimeline(props: SegmentTimelinePr
               }
             />
           )}
+          {initDatasourceDateRangeState.isLoading() && <Loader />}
         </div>
       </ResizeSensor>
     </div>
diff --git a/web-console/src/views/datasources-view/datasources-view.tsx 
b/web-console/src/views/datasources-view/datasources-view.tsx
index de30832ce39..e79592d3aa5 100644
--- a/web-console/src/views/datasources-view/datasources-view.tsx
+++ b/web-console/src/views/datasources-view/datasources-view.tsx
@@ -1147,7 +1147,7 @@ GROUP BY 1, 2`;
   }
 
   private renderDatasourcesTable() {
-    const { goToSegments, goToTasks, capabilities, filters, onFiltersChange } 
= this.props;
+    const { goToTasks, capabilities, filters, onFiltersChange } = this.props;
     const { datasourcesAndDefaultRulesState, showUnused, visibleColumns, 
showSegmentTimeline } =
       this.state;
 
@@ -1240,7 +1240,10 @@ GROUP BY 1, 2`;
               const hasZeroReplicationRule = 
RuleUtil.hasZeroReplicaRule(rules, defaultRules);
               const descriptor = hasZeroReplicationRule ? 'pre-cached' : 
'available';
               const segmentsEl = (
-                <a onClick={() => goToSegments(datasource)}>
+                <a
+                  onClick={() => this.setState({ showSegmentTimeline: { 
datasource } })}
+                  data-tooltip="Show in segment timeline"
+                >
                   {pluralIfNeeded(num_segments, 'segment')}
                 </a>
               );
@@ -1742,7 +1745,7 @@ GROUP BY 1, 2`;
           {showSegmentTimeline && (
             <SegmentTimeline
               capabilities={capabilities}
-              initDatasource={showSegmentTimeline.datasource}
+              datasource={showSegmentTimeline.datasource}
             />
           )}
           {this.renderDatasourcesTable()}
diff --git a/web-console/src/views/segments-view/segments-view.tsx 
b/web-console/src/views/segments-view/segments-view.tsx
index 98e8fa44bb0..6f3cd468360 100644
--- a/web-console/src/views/segments-view/segments-view.tsx
+++ b/web-console/src/views/segments-view/segments-view.tsx
@@ -1054,7 +1054,7 @@ export class SegmentsView extends 
React.PureComponent<SegmentsViewProps, Segment
           secondaryMinSize={10}
         >
           {showSegmentTimeline && (
-            <SegmentTimeline capabilities={capabilities} 
initDatasource={undefined} />
+            <SegmentTimeline capabilities={capabilities} 
datasource={undefined} />
           )}
           {this.renderSegmentsTable()}
         </SplitterLayout>


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

Reply via email to