mirror of
https://github.com/SigNoz/signoz.git
synced 2026-06-01 23:00:29 +01:00
Compare commits
3 Commits
fix/alert-
...
nv/5122
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d3ffefd15a | ||
|
|
fe8283e4de | ||
|
|
4366d6fd96 |
@@ -11,7 +11,6 @@
|
||||
}
|
||||
|
||||
.divider {
|
||||
border-color: var(--l1-border);
|
||||
margin: 16px 0;
|
||||
margin-top: 10px;
|
||||
--divider-color: var(--l1-border);
|
||||
--divider-margin: 10px 0 16px 0;
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ import cx from 'classnames';
|
||||
import { ENTITY_VERSION_V4, ENTITY_VERSION_V5 } from 'constants/app';
|
||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||
import QBEntityOptions from 'container/QueryBuilder/components/QBEntityOptions/QBEntityOptions';
|
||||
import { QueryProps } from 'container/QueryBuilder/components/Query/Query.interfaces';
|
||||
import { QueryProps } from 'container/QueryBuilder/type';
|
||||
import SpanScopeSelector from 'container/QueryBuilder/filters/QueryBuilderSearchV2/SpanScopeSelector';
|
||||
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
|
||||
import { useQueryOperations } from 'hooks/queryBuilder/useQueryBuilderOperations';
|
||||
|
||||
@@ -23,6 +23,10 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__divider {
|
||||
--divider-vertical-margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.hide-update {
|
||||
@@ -55,12 +59,6 @@
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.ant-divider {
|
||||
margin: 0;
|
||||
height: 28px;
|
||||
border: 1px solid var(--l1-border);
|
||||
}
|
||||
}
|
||||
|
||||
.explorer-options {
|
||||
|
||||
@@ -874,7 +874,9 @@ function ExplorerOptions({
|
||||
<>
|
||||
<Divider
|
||||
type="vertical"
|
||||
className={isEditDeleteSupported ? '' : 'hidden'}
|
||||
className={cx('explorer-options-container__divider', {
|
||||
hidden: !isEditDeleteSupported,
|
||||
})}
|
||||
/>
|
||||
<Tooltip title="Update this view" placement="top">
|
||||
<Button
|
||||
|
||||
@@ -94,11 +94,8 @@
|
||||
margin-bottom: 24px;
|
||||
width: 100%;
|
||||
|
||||
.ant-divider::before,
|
||||
.ant-divider::after {
|
||||
border-bottom: 2px dotted var(--l1-border);
|
||||
border-top: 2px dotted var(--l1-border);
|
||||
height: 8px;
|
||||
&__divider {
|
||||
--divider-border-width: 1px;
|
||||
}
|
||||
|
||||
.ant-typography {
|
||||
|
||||
@@ -125,7 +125,7 @@ export function AlertsEmptyState(): JSX.Element {
|
||||
</div>
|
||||
</section>
|
||||
<div className="get-started-text">
|
||||
<Divider>
|
||||
<Divider className="get-started-text__divider">
|
||||
<Typography.Text className="get-started-text">
|
||||
Or get started with these sample alerts
|
||||
</Typography.Text>
|
||||
|
||||
@@ -19,9 +19,9 @@
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
.ant-divider {
|
||||
margin: 8px 0 !important;
|
||||
border: 0.5px solid var(--l1-border);
|
||||
&__divider {
|
||||
--divider-color: var(--l1-border);
|
||||
--divider-margin: 8px 0;
|
||||
}
|
||||
|
||||
.explorer-columns-contents {
|
||||
|
||||
@@ -234,7 +234,7 @@ function ExplorerColumnsRenderer({
|
||||
</Tooltip>
|
||||
)}
|
||||
</div>
|
||||
<Divider />
|
||||
<Divider className="explorer-columns-renderer__divider" />
|
||||
{!isError && (
|
||||
<div className="explorer-columns-contents">
|
||||
<DragDropContext onDragEnd={onDragEnd}>
|
||||
|
||||
@@ -19,12 +19,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
.ant-modal-body {
|
||||
.ant-divider {
|
||||
margin: 16px 0;
|
||||
border: 0.5px solid var(--l1-border);
|
||||
}
|
||||
}
|
||||
.downtime-schedule-btn {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
import { QueryBuilderProps } from 'container/QueryBuilder/QueryBuilder.interfaces';
|
||||
import { IBuilderQuery } from 'types/api/queryBuilder/queryBuilderData';
|
||||
|
||||
export type QueryProps = {
|
||||
index: number;
|
||||
isAvailableToDisable: boolean;
|
||||
query: IBuilderQuery;
|
||||
queryVariant?: 'static' | 'dropdown';
|
||||
isListViewPanel?: boolean;
|
||||
showFunctions?: boolean;
|
||||
version: string;
|
||||
showSpanScopeSelector?: boolean;
|
||||
showOnlyWhereClause?: boolean;
|
||||
showTraceOperator?: boolean;
|
||||
hasTraceOperator?: boolean;
|
||||
signalSource?: string;
|
||||
isMultiQueryAllowed?: boolean;
|
||||
} & Pick<QueryBuilderProps, 'filterConfigs' | 'queryComponents'>;
|
||||
@@ -1,8 +0,0 @@
|
||||
.qb-search-container {
|
||||
display: block;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.qb-container {
|
||||
padding: 0 24px;
|
||||
}
|
||||
@@ -1,628 +0,0 @@
|
||||
/* eslint-disable sonarjs/cognitive-complexity */
|
||||
// ** Hooks
|
||||
import {
|
||||
ChangeEvent,
|
||||
memo,
|
||||
ReactNode,
|
||||
useCallback,
|
||||
useMemo,
|
||||
useState,
|
||||
} from 'react';
|
||||
import { useLocation } from 'react-use';
|
||||
import { Col, Input, Row, Tooltip } from 'antd';
|
||||
import { Typography } from '@signozhq/ui/typography';
|
||||
import { ENTITY_VERSION_V4 } from 'constants/app';
|
||||
// ** Constants
|
||||
import { ATTRIBUTE_TYPES, PANEL_TYPES } from 'constants/queryBuilder';
|
||||
import ROUTES from 'constants/routes';
|
||||
// ** Components
|
||||
import {
|
||||
AdditionalFiltersToggler,
|
||||
DataSourceDropdown,
|
||||
FilterLabel,
|
||||
} from 'container/QueryBuilder/components';
|
||||
import {
|
||||
AggregatorFilter,
|
||||
GroupByFilter,
|
||||
HavingFilter,
|
||||
MetricNameSelector,
|
||||
OperatorsSelect,
|
||||
OrderByFilter,
|
||||
ReduceToFilter,
|
||||
} from 'container/QueryBuilder/filters';
|
||||
import AggregateEveryFilter from 'container/QueryBuilder/filters/AggregateEveryFilter';
|
||||
import LimitFilter from 'container/QueryBuilder/filters/LimitFilter/LimitFilter';
|
||||
import QueryBuilderSearch from 'container/QueryBuilder/filters/QueryBuilderSearch';
|
||||
import QueryBuilderSearchV2 from 'container/QueryBuilder/filters/QueryBuilderSearchV2/QueryBuilderSearchV2';
|
||||
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
|
||||
import { useQueryOperations } from 'hooks/queryBuilder/useQueryBuilderOperations';
|
||||
import { IBuilderQuery } from 'types/api/queryBuilder/queryBuilderData';
|
||||
import { DataSource } from 'types/common/queryBuilder';
|
||||
import { transformToUpperCase } from 'utils/transformToUpperCase';
|
||||
|
||||
import QBEntityOptions from '../QBEntityOptions/QBEntityOptions';
|
||||
import SpaceAggregationOptions from '../SpaceAggregationOptions/SpaceAggregationOptions';
|
||||
// ** Types
|
||||
import { QueryProps } from './Query.interfaces';
|
||||
|
||||
import './Query.styles.scss';
|
||||
|
||||
export const Query = memo(function Query({
|
||||
index,
|
||||
queryVariant,
|
||||
query,
|
||||
filterConfigs,
|
||||
queryComponents,
|
||||
isListViewPanel = false,
|
||||
showFunctions = false,
|
||||
version,
|
||||
}: QueryProps): JSX.Element {
|
||||
const { panelType, currentQuery, cloneQuery } = useQueryBuilder();
|
||||
const { pathname } = useLocation();
|
||||
|
||||
const [isCollapse, setIsCollapsed] = useState(false);
|
||||
|
||||
const {
|
||||
operators,
|
||||
spaceAggregationOptions,
|
||||
isMetricsDataSource,
|
||||
isTracePanelType,
|
||||
listOfAdditionalFilters,
|
||||
handleChangeAggregatorAttribute,
|
||||
handleChangeQueryData,
|
||||
handleChangeDataSource,
|
||||
handleChangeOperator,
|
||||
handleSpaceAggregationChange,
|
||||
handleDeleteQuery,
|
||||
handleQueryFunctionsUpdates,
|
||||
} = useQueryOperations({
|
||||
index,
|
||||
query,
|
||||
filterConfigs,
|
||||
isListViewPanel,
|
||||
entityVersion: version,
|
||||
});
|
||||
|
||||
const handleChangeAggregateEvery = useCallback(
|
||||
(value: IBuilderQuery['stepInterval']) => {
|
||||
handleChangeQueryData('stepInterval', value);
|
||||
},
|
||||
[handleChangeQueryData],
|
||||
);
|
||||
|
||||
const handleChangeLimit = useCallback(
|
||||
(value: IBuilderQuery['limit']) => {
|
||||
handleChangeQueryData('limit', value);
|
||||
},
|
||||
[handleChangeQueryData],
|
||||
);
|
||||
|
||||
const handleChangeHavingFilter = useCallback(
|
||||
(value: IBuilderQuery['having']) => {
|
||||
handleChangeQueryData('having', value);
|
||||
},
|
||||
[handleChangeQueryData],
|
||||
);
|
||||
|
||||
const handleChangeOrderByKeys = useCallback(
|
||||
(value: IBuilderQuery['orderBy']) => {
|
||||
handleChangeQueryData('orderBy', value);
|
||||
},
|
||||
[handleChangeQueryData],
|
||||
);
|
||||
|
||||
const handleToggleDisableQuery = useCallback(() => {
|
||||
handleChangeQueryData('disabled', !query.disabled);
|
||||
}, [handleChangeQueryData, query]);
|
||||
|
||||
const handleChangeTagFilters = useCallback(
|
||||
(value: IBuilderQuery['filters']) => {
|
||||
handleChangeQueryData('filters', value);
|
||||
},
|
||||
[handleChangeQueryData],
|
||||
);
|
||||
|
||||
const handleChangeReduceTo = useCallback(
|
||||
(value: IBuilderQuery['reduceTo']) => {
|
||||
handleChangeQueryData('reduceTo', value);
|
||||
},
|
||||
[handleChangeQueryData],
|
||||
);
|
||||
|
||||
const handleChangeGroupByKeys = useCallback(
|
||||
(value: IBuilderQuery['groupBy']) => {
|
||||
handleChangeQueryData('groupBy', value);
|
||||
},
|
||||
[handleChangeQueryData],
|
||||
);
|
||||
|
||||
const handleChangeQueryLegend = useCallback(
|
||||
(event: ChangeEvent<HTMLInputElement>) => {
|
||||
handleChangeQueryData('legend', event.target.value);
|
||||
},
|
||||
[handleChangeQueryData],
|
||||
);
|
||||
|
||||
const handleToggleCollapsQuery = (): void => {
|
||||
setIsCollapsed(!isCollapse);
|
||||
};
|
||||
|
||||
const renderOrderByFilter = useCallback((): ReactNode => {
|
||||
if (queryComponents?.renderOrderBy) {
|
||||
return queryComponents.renderOrderBy({
|
||||
query,
|
||||
onChange: handleChangeOrderByKeys,
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<OrderByFilter
|
||||
entityVersion={version}
|
||||
query={query}
|
||||
onChange={handleChangeOrderByKeys}
|
||||
isListViewPanel={isListViewPanel}
|
||||
/>
|
||||
);
|
||||
}, [
|
||||
queryComponents,
|
||||
query,
|
||||
version,
|
||||
handleChangeOrderByKeys,
|
||||
isListViewPanel,
|
||||
]);
|
||||
|
||||
const renderAggregateEveryFilter = useCallback(
|
||||
(): JSX.Element | null =>
|
||||
!filterConfigs?.stepInterval?.isHidden ? (
|
||||
<Row gutter={[11, 5]}>
|
||||
<Col flex="5.93rem">
|
||||
<FilterLabel label="Aggregate Every" />
|
||||
</Col>
|
||||
<Col flex="1 1 6rem">
|
||||
<AggregateEveryFilter
|
||||
query={query}
|
||||
disabled={filterConfigs?.stepInterval?.isDisabled || false}
|
||||
onChange={handleChangeAggregateEvery}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
) : null,
|
||||
[
|
||||
filterConfigs?.stepInterval?.isHidden,
|
||||
filterConfigs?.stepInterval?.isDisabled,
|
||||
query,
|
||||
handleChangeAggregateEvery,
|
||||
],
|
||||
);
|
||||
|
||||
const isExplorerPage = useMemo(
|
||||
() =>
|
||||
pathname === ROUTES.LOGS_EXPLORER || pathname === ROUTES.TRACES_EXPLORER,
|
||||
[pathname],
|
||||
);
|
||||
|
||||
const renderAdditionalFilters = useCallback((): ReactNode => {
|
||||
switch (panelType) {
|
||||
case PANEL_TYPES.TIME_SERIES: {
|
||||
return (
|
||||
<>
|
||||
<Col span={11}>
|
||||
<Row gutter={[11, 5]}>
|
||||
<Col flex="5.93rem">
|
||||
<FilterLabel label="Limit" />
|
||||
</Col>
|
||||
<Col flex="1 1 12.5rem">
|
||||
<LimitFilter query={query} onChange={handleChangeLimit} />
|
||||
</Col>
|
||||
</Row>
|
||||
</Col>
|
||||
<Col span={24}>
|
||||
<Row gutter={[11, 5]}>
|
||||
<Col flex="5.93rem">
|
||||
<FilterLabel label="HAVING" />
|
||||
</Col>
|
||||
<Col flex="1 1 12.5rem">
|
||||
<HavingFilter
|
||||
entityVersion={version}
|
||||
onChange={handleChangeHavingFilter}
|
||||
query={query}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
</Col>
|
||||
<Col span={11}>
|
||||
<Row gutter={[11, 5]}>
|
||||
<Col flex="5.93rem">
|
||||
<FilterLabel label="Order by" />
|
||||
</Col>
|
||||
<Col flex="1 1 12.5rem">{renderOrderByFilter()}</Col>
|
||||
</Row>
|
||||
</Col>
|
||||
|
||||
<Col span={11}>{renderAggregateEveryFilter()}</Col>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
case PANEL_TYPES.VALUE: {
|
||||
return (
|
||||
<>
|
||||
<Col span={11}>
|
||||
<Row gutter={[11, 5]}>
|
||||
<Col flex="5.93rem">
|
||||
<FilterLabel label="HAVING" />
|
||||
</Col>
|
||||
<Col flex="1 1 12.5rem">
|
||||
<HavingFilter
|
||||
onChange={handleChangeHavingFilter}
|
||||
entityVersion={version}
|
||||
query={query}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
</Col>
|
||||
<Col span={11}>{renderAggregateEveryFilter()}</Col>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
default: {
|
||||
return (
|
||||
<>
|
||||
{!filterConfigs?.limit?.isHidden && (
|
||||
<Col span={11}>
|
||||
<Row gutter={[11, 5]}>
|
||||
<Col flex="5.93rem">
|
||||
<FilterLabel label="Limit" />
|
||||
</Col>
|
||||
<Col flex="1 1 12.5rem">
|
||||
<LimitFilter query={query} onChange={handleChangeLimit} />
|
||||
</Col>
|
||||
</Row>
|
||||
</Col>
|
||||
)}
|
||||
|
||||
{!filterConfigs?.having?.isHidden && (
|
||||
<Col span={11}>
|
||||
<Row gutter={[11, 5]}>
|
||||
<Col flex="5.93rem">
|
||||
<FilterLabel label="HAVING" />
|
||||
</Col>
|
||||
<Col flex="1 1 12.5rem">
|
||||
<HavingFilter
|
||||
entityVersion={version}
|
||||
onChange={handleChangeHavingFilter}
|
||||
query={query}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
</Col>
|
||||
)}
|
||||
<Col span={11}>
|
||||
<Row gutter={[11, 5]}>
|
||||
<Col flex="5.93rem">
|
||||
<FilterLabel label="Order by" />
|
||||
</Col>
|
||||
<Col flex="1 1 12.5rem">{renderOrderByFilter()}</Col>
|
||||
</Row>
|
||||
</Col>
|
||||
|
||||
<Col span={11}>{renderAggregateEveryFilter()}</Col>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
}, [
|
||||
panelType,
|
||||
query,
|
||||
handleChangeLimit,
|
||||
version,
|
||||
handleChangeHavingFilter,
|
||||
renderOrderByFilter,
|
||||
renderAggregateEveryFilter,
|
||||
filterConfigs?.limit?.isHidden,
|
||||
filterConfigs?.having?.isHidden,
|
||||
]);
|
||||
|
||||
const disableOperatorSelector =
|
||||
!query?.aggregateAttribute?.key || query?.aggregateAttribute?.key === '';
|
||||
|
||||
const isVersionV4 = version && version === ENTITY_VERSION_V4;
|
||||
|
||||
return (
|
||||
<Row gutter={[0, 12]} className={`query-builder-${version}`}>
|
||||
<QBEntityOptions
|
||||
isMetricsDataSource={isMetricsDataSource}
|
||||
showFunctions={
|
||||
(version && version === ENTITY_VERSION_V4) ||
|
||||
query.dataSource === DataSource.LOGS ||
|
||||
showFunctions ||
|
||||
false
|
||||
}
|
||||
isCollapsed={isCollapse}
|
||||
entityType="query"
|
||||
entityData={query}
|
||||
onToggleVisibility={handleToggleDisableQuery}
|
||||
onDelete={handleDeleteQuery}
|
||||
onCloneQuery={cloneQuery}
|
||||
onCollapseEntity={handleToggleCollapsQuery}
|
||||
query={query}
|
||||
onQueryFunctionsUpdates={handleQueryFunctionsUpdates}
|
||||
showDeleteButton={currentQuery.builder.queryData.length > 1}
|
||||
isListViewPanel={isListViewPanel}
|
||||
index={index}
|
||||
queryVariant={queryVariant}
|
||||
/>
|
||||
|
||||
{!isCollapse && (
|
||||
<Row gutter={[0, 12]} className="qb-container">
|
||||
<Col span={24}>
|
||||
<Row align="middle" gutter={[5, 11]}>
|
||||
{!isExplorerPage && (
|
||||
<Col>
|
||||
{queryVariant === 'dropdown' ? (
|
||||
<DataSourceDropdown
|
||||
onChange={handleChangeDataSource}
|
||||
value={query.dataSource}
|
||||
style={{ minWidth: '5.625rem' }}
|
||||
isListViewPanel={isListViewPanel}
|
||||
/>
|
||||
) : (
|
||||
<FilterLabel label={transformToUpperCase(query.dataSource)} />
|
||||
)}
|
||||
</Col>
|
||||
)}
|
||||
|
||||
{isMetricsDataSource && (
|
||||
<Col span={12}>
|
||||
<Row gutter={[11, 5]}>
|
||||
{version && version === 'v3' && (
|
||||
<Col flex="5.93rem">
|
||||
<Tooltip
|
||||
title={
|
||||
<div style={{ textAlign: 'center' }}>
|
||||
Select Aggregate Operator
|
||||
<Typography.Link
|
||||
className="learn-more"
|
||||
href="https://signoz.io/docs/userguide/query-builder/?utm_source=product&utm_medium=query-builder#aggregation"
|
||||
target="_blank"
|
||||
style={{ textDecoration: 'underline' }}
|
||||
>
|
||||
{' '}
|
||||
<br />
|
||||
Learn more
|
||||
</Typography.Link>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<OperatorsSelect
|
||||
value={query.aggregateOperator || ''}
|
||||
onChange={handleChangeOperator}
|
||||
operators={operators}
|
||||
/>
|
||||
</Tooltip>
|
||||
</Col>
|
||||
)}
|
||||
|
||||
<Col flex="auto">
|
||||
<MetricNameSelector
|
||||
onChange={handleChangeAggregatorAttribute}
|
||||
query={query}
|
||||
/>
|
||||
</Col>
|
||||
|
||||
{version &&
|
||||
version === ENTITY_VERSION_V4 &&
|
||||
operators &&
|
||||
Array.isArray(operators) &&
|
||||
operators.length > 0 && (
|
||||
<Col flex="5.93rem">
|
||||
<Tooltip
|
||||
title={
|
||||
<div style={{ textAlign: 'center' }}>
|
||||
Select Aggregate Operator
|
||||
<Typography.Link
|
||||
className="learn-more"
|
||||
href="https://signoz.io/docs/metrics-management/types-and-aggregation/?utm_source=product&utm_medium=query-builder#aggregation"
|
||||
target="_blank"
|
||||
style={{ textDecoration: 'underline' }}
|
||||
>
|
||||
{' '}
|
||||
<br />
|
||||
Learn more
|
||||
</Typography.Link>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<OperatorsSelect
|
||||
value={query.aggregateOperator || ''}
|
||||
onChange={handleChangeOperator}
|
||||
operators={operators}
|
||||
disabled={disableOperatorSelector}
|
||||
/>
|
||||
</Tooltip>
|
||||
</Col>
|
||||
)}
|
||||
</Row>
|
||||
</Col>
|
||||
)}
|
||||
|
||||
<Col flex="1 1 40rem">
|
||||
<Row gutter={[11, 5]}>
|
||||
{isMetricsDataSource && (
|
||||
<Col>
|
||||
<FilterLabel label="WHERE" />
|
||||
</Col>
|
||||
)}
|
||||
<Col flex="1" className="qb-search-container">
|
||||
{[DataSource.LOGS, DataSource.TRACES].includes(query.dataSource) ? (
|
||||
<QueryBuilderSearchV2
|
||||
query={query}
|
||||
onChange={handleChangeTagFilters}
|
||||
whereClauseConfig={filterConfigs?.filters}
|
||||
hideSpanScopeSelector={query.dataSource !== DataSource.TRACES}
|
||||
/>
|
||||
) : (
|
||||
<QueryBuilderSearch
|
||||
query={query}
|
||||
onChange={handleChangeTagFilters}
|
||||
whereClauseConfig={filterConfigs?.filters}
|
||||
/>
|
||||
)}
|
||||
</Col>
|
||||
</Row>
|
||||
</Col>
|
||||
</Row>
|
||||
</Col>
|
||||
{!isMetricsDataSource && !isListViewPanel && (
|
||||
<Col span={11}>
|
||||
<Row gutter={[11, 5]}>
|
||||
<Col flex="5.93rem">
|
||||
<Tooltip
|
||||
title={
|
||||
<div style={{ textAlign: 'center' }}>
|
||||
Select Aggregate Operator
|
||||
<Typography.Link
|
||||
href="https://signoz.io/docs/userguide/query-builder/?utm_source=product&utm_medium=query-builder#aggregation"
|
||||
target="_blank"
|
||||
style={{ textDecoration: 'underline' }}
|
||||
>
|
||||
{' '}
|
||||
<br />
|
||||
Learn more
|
||||
</Typography.Link>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<OperatorsSelect
|
||||
value={query.aggregateOperator || ''}
|
||||
onChange={handleChangeOperator}
|
||||
operators={operators}
|
||||
/>
|
||||
</Tooltip>
|
||||
</Col>
|
||||
<Col flex="1 1 12.5rem">
|
||||
<AggregatorFilter
|
||||
query={query}
|
||||
onChange={handleChangeAggregatorAttribute}
|
||||
disabled={
|
||||
panelType === PANEL_TYPES.LIST || panelType === PANEL_TYPES.TRACE
|
||||
}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
</Col>
|
||||
)}
|
||||
{!isListViewPanel && (
|
||||
<Col span={24}>
|
||||
<Row gutter={[11, 5]}>
|
||||
<Col flex="5.93rem">
|
||||
{isVersionV4 && isMetricsDataSource ? (
|
||||
<SpaceAggregationOptions
|
||||
panelType={panelType}
|
||||
key={`${panelType}${query.spaceAggregation}${query.timeAggregation}`}
|
||||
aggregatorAttributeType={
|
||||
query?.aggregateAttribute?.type as ATTRIBUTE_TYPES
|
||||
}
|
||||
selectedValue={query.spaceAggregation}
|
||||
disabled={disableOperatorSelector}
|
||||
onSelect={handleSpaceAggregationChange}
|
||||
operators={spaceAggregationOptions}
|
||||
/>
|
||||
) : (
|
||||
<FilterLabel
|
||||
label={panelType === PANEL_TYPES.VALUE ? 'Reduce to' : 'Group by'}
|
||||
/>
|
||||
)}
|
||||
</Col>
|
||||
|
||||
<Col flex="1 1 12.5rem">
|
||||
{panelType === PANEL_TYPES.VALUE ? (
|
||||
<Row>
|
||||
{isVersionV4 && isMetricsDataSource && (
|
||||
<Col span={4}>
|
||||
<FilterLabel label="Reduce to" />
|
||||
</Col>
|
||||
)}
|
||||
<Col span={isVersionV4 && isMetricsDataSource ? 20 : 24}>
|
||||
<ReduceToFilter query={query} onChange={handleChangeReduceTo} />
|
||||
</Col>
|
||||
</Row>
|
||||
) : (
|
||||
<GroupByFilter
|
||||
disabled={isMetricsDataSource && !query.aggregateAttribute?.key}
|
||||
query={query}
|
||||
onChange={handleChangeGroupByKeys}
|
||||
/>
|
||||
)}
|
||||
</Col>
|
||||
|
||||
{isVersionV4 &&
|
||||
isMetricsDataSource &&
|
||||
(panelType === PANEL_TYPES.TABLE || panelType === PANEL_TYPES.PIE) && (
|
||||
<Col flex="1 1 12.5rem">
|
||||
<Row>
|
||||
<Col span={6}>
|
||||
<FilterLabel label="Reduce to" />
|
||||
</Col>
|
||||
|
||||
<Col span={18}>
|
||||
<ReduceToFilter query={query} onChange={handleChangeReduceTo} />
|
||||
</Col>
|
||||
</Row>
|
||||
</Col>
|
||||
)}
|
||||
</Row>
|
||||
</Col>
|
||||
)}
|
||||
{!isTracePanelType && !isListViewPanel && (
|
||||
<Col span={24}>
|
||||
<AdditionalFiltersToggler
|
||||
listOfAdditionalFilter={listOfAdditionalFilters}
|
||||
>
|
||||
<Row gutter={[0, 11]} justify="space-between">
|
||||
{renderAdditionalFilters()}
|
||||
</Row>
|
||||
</AdditionalFiltersToggler>
|
||||
</Col>
|
||||
)}
|
||||
{isListViewPanel && (
|
||||
<Col span={24}>
|
||||
<Row gutter={[0, 11]} justify="space-between">
|
||||
{renderAdditionalFilters()}
|
||||
</Row>
|
||||
</Col>
|
||||
)}
|
||||
{panelType !== PANEL_TYPES.LIST && panelType !== PANEL_TYPES.TRACE && (
|
||||
<Row style={{ width: '100%' }}>
|
||||
<Tooltip
|
||||
placement="right"
|
||||
title={
|
||||
<div style={{ textAlign: 'center' }}>
|
||||
Name of legend
|
||||
<Typography.Link
|
||||
style={{ textDecoration: 'underline' }}
|
||||
href="https://signoz.io/docs/userguide/query-builder/?utm_source=product&utm_medium=query-builder#legend-format"
|
||||
target="_blank"
|
||||
>
|
||||
{' '}
|
||||
<br />
|
||||
Learn more
|
||||
</Typography.Link>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<Input
|
||||
onChange={handleChangeQueryLegend}
|
||||
size="middle"
|
||||
value={query.legend}
|
||||
addonBefore="Legend Format"
|
||||
/>
|
||||
</Tooltip>
|
||||
</Row>
|
||||
)}
|
||||
</Row>
|
||||
)}
|
||||
</Row>
|
||||
);
|
||||
});
|
||||
@@ -1 +0,0 @@
|
||||
export { Query } from './Query';
|
||||
@@ -5,4 +5,3 @@ export { Formula } from './Formula';
|
||||
export { HavingFilterTag } from './HavingFilterTag';
|
||||
export { ListItemWrapper } from './ListItemWrapper';
|
||||
export { ListMarker } from './ListMarker';
|
||||
export { Query } from './Query';
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import { IQueryBuilderState } from 'constants/queryBuilder';
|
||||
import { QueryBuilderProps } from 'container/QueryBuilder/QueryBuilder.interfaces';
|
||||
import { IBuilderQuery } from 'types/api/queryBuilder/queryBuilderData';
|
||||
|
||||
export interface InitialStateI {
|
||||
search: string;
|
||||
@@ -18,3 +20,19 @@ export type Option = {
|
||||
isIndexed?: boolean;
|
||||
type?: string;
|
||||
};
|
||||
|
||||
export type QueryProps = {
|
||||
index: number;
|
||||
isAvailableToDisable: boolean;
|
||||
query: IBuilderQuery;
|
||||
queryVariant?: 'static' | 'dropdown';
|
||||
isListViewPanel?: boolean;
|
||||
showFunctions?: boolean;
|
||||
version: string;
|
||||
showSpanScopeSelector?: boolean;
|
||||
showOnlyWhereClause?: boolean;
|
||||
showTraceOperator?: boolean;
|
||||
hasTraceOperator?: boolean;
|
||||
signalSource?: string;
|
||||
isMultiQueryAllowed?: boolean;
|
||||
} & Pick<QueryBuilderProps, 'filterConfigs' | 'queryComponents'>;
|
||||
|
||||
@@ -191,13 +191,6 @@
|
||||
line-height: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.ant-modal-body {
|
||||
.ant-divider {
|
||||
margin: 16px 0;
|
||||
border: 0.5px solid var(--l1-border);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.create-policy-container {
|
||||
|
||||
@@ -15,11 +15,8 @@
|
||||
}
|
||||
}
|
||||
|
||||
.ant-divider {
|
||||
margin-inline-start: 10px !important;
|
||||
margin-inline-end: 16px !important;
|
||||
height: 16px;
|
||||
border-color: var(--l1-border);
|
||||
&__divider {
|
||||
--divider-vertical-margin: 10px;
|
||||
}
|
||||
.ant-drawer-close {
|
||||
margin: 0 !important;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useCallback, useMemo, useState } from 'react';
|
||||
import { Color, Spacing } from '@signozhq/design-tokens';
|
||||
import { Color } from '@signozhq/design-tokens';
|
||||
import { Button, Drawer } from 'antd';
|
||||
import { Divider } from '@signozhq/ui/divider';
|
||||
import { Typography } from '@signozhq/ui/typography';
|
||||
@@ -179,7 +179,10 @@ function SpanRelatedSignals({
|
||||
width="50%"
|
||||
title={
|
||||
<>
|
||||
<Divider type="vertical" />
|
||||
<Divider
|
||||
type="vertical"
|
||||
className="span-related-signals-drawer__divider"
|
||||
/>
|
||||
<Typography.Text className="title">
|
||||
Related Signals - {selectedSpan.name}
|
||||
</Typography.Text>
|
||||
@@ -194,7 +197,7 @@ function SpanRelatedSignals({
|
||||
}}
|
||||
className="span-related-signals-drawer"
|
||||
destroyOnClose
|
||||
closeIcon={<X size={16} style={{ marginTop: Spacing.MARGIN_1 }} />}
|
||||
closeIcon={<X size={16} />}
|
||||
>
|
||||
{selectedSpan && (
|
||||
<div className="span-related-signals-drawer__content">
|
||||
|
||||
@@ -41,9 +41,9 @@
|
||||
}
|
||||
|
||||
.alert-details {
|
||||
.divider {
|
||||
border-color: var(--l1-border);
|
||||
margin: 16px 0;
|
||||
&__divider {
|
||||
--divider-color: var(--l1-border);
|
||||
--divider-margin: 16px 0;
|
||||
}
|
||||
.breadcrumb-divider {
|
||||
margin-top: 10px;
|
||||
|
||||
@@ -104,7 +104,7 @@ function AlertDetails(): JSX.Element {
|
||||
/>
|
||||
|
||||
{alertRuleDetails && <AlertHeader alertDetails={alertRuleDetails} />}
|
||||
<Divider className="divider" />
|
||||
<Divider className="alert-details__divider" />
|
||||
<div className="tabs-and-filters">
|
||||
<RouteTab
|
||||
routes={routes}
|
||||
|
||||
@@ -3,10 +3,9 @@
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
color: var(--l1-border);
|
||||
.ant-divider-vertical {
|
||||
height: 16px;
|
||||
border-color: var(--l1-border);
|
||||
margin: 0;
|
||||
&__divider {
|
||||
--divider-color: var(--l1-border);
|
||||
--divider-vertical-margin: 0;
|
||||
}
|
||||
.dropdown-trigger-wrapper {
|
||||
display: flex;
|
||||
|
||||
@@ -122,7 +122,7 @@ function AlertActionButtons({
|
||||
</Tooltip>
|
||||
<CopyToClipboard textToCopy={window.location.href} />
|
||||
|
||||
<Divider type="vertical" />
|
||||
<Divider type="vertical" className="alert-action-buttons__divider" />
|
||||
|
||||
<DropdownMenuSimple menu={{ items: menuItems }}>
|
||||
<span className="dropdown-trigger-wrapper">
|
||||
|
||||
@@ -47,10 +47,9 @@
|
||||
}
|
||||
}
|
||||
|
||||
.divider {
|
||||
background-color: var(--l1-border);
|
||||
margin: 0;
|
||||
border-color: var(--l1-border);
|
||||
.section-body__divider {
|
||||
--divider-color: var(--l1-border);
|
||||
--divider-margin: 0;
|
||||
}
|
||||
|
||||
.filter-header {
|
||||
|
||||
@@ -65,7 +65,7 @@ export function Section(props: SectionProps): JSX.Element {
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Divider plain className="divider" />
|
||||
<Divider plain className="section-body__divider" />
|
||||
<div className="section-body-header" data-testid={`collapse-${panelName}`}>
|
||||
<Collapse
|
||||
bordered={false}
|
||||
|
||||
@@ -24,9 +24,6 @@
|
||||
align-items: center;
|
||||
}
|
||||
gap: 12px;
|
||||
.ant-divider-vertical {
|
||||
margin: 0;
|
||||
}
|
||||
.funnel-configuration__rename-btn {
|
||||
padding: 4px;
|
||||
width: 24px;
|
||||
@@ -74,4 +71,7 @@
|
||||
gap: 12px;
|
||||
padding: 16px;
|
||||
}
|
||||
&__divider {
|
||||
--divider-margin: 0px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,7 +77,7 @@ function FunnelConfiguration({
|
||||
/>
|
||||
</Tooltip>
|
||||
<CopyToClipboard textToCopy={window.location.href} />
|
||||
<Divider type="vertical" />
|
||||
<Divider type="vertical" className="funnel-configuration__divider" />
|
||||
<FunnelItemPopover
|
||||
isPopoverOpen={isPopoverOpen}
|
||||
setIsPopoverOpen={setIsPopoverOpen}
|
||||
|
||||
@@ -94,9 +94,6 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.ant-divider-vertical {
|
||||
margin: 0 12px;
|
||||
}
|
||||
.funnel-item__action-btn {
|
||||
border: none;
|
||||
padding: 4px;
|
||||
|
||||
@@ -26,10 +26,7 @@
|
||||
}
|
||||
&__divider {
|
||||
width: 100%;
|
||||
.ant-divider {
|
||||
margin: 0;
|
||||
border-color: var(--l1-border);
|
||||
}
|
||||
--divider-margin: 0;
|
||||
}
|
||||
&__latency-options {
|
||||
flex-shrink: 0;
|
||||
|
||||
@@ -17,14 +17,6 @@
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
&__divider {
|
||||
width: 100%;
|
||||
.ant-divider {
|
||||
margin: 0;
|
||||
border-color: var(--l1-border);
|
||||
}
|
||||
}
|
||||
|
||||
&__time-range {
|
||||
width: 100%;
|
||||
height: 32px;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { QueryProps } from 'container/QueryBuilder/components/Query/Query.interfaces';
|
||||
import { QueryProps } from 'container/QueryBuilder/type';
|
||||
import { QueryBuilderProps } from 'container/QueryBuilder/QueryBuilder.interfaces';
|
||||
import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse';
|
||||
import {
|
||||
|
||||
@@ -135,6 +135,9 @@ type seriesLookup struct {
|
||||
data map[string]map[int64]float64
|
||||
// seriesKey -> original series for metadata preservation
|
||||
seriesMetadata map[string]*TimeSeries
|
||||
// maps a variable to its series keys, letting evaluation iterate a single
|
||||
// variable's series directly.
|
||||
variableToSeriesKeys map[string][]string
|
||||
}
|
||||
|
||||
// FormulaEvaluator handles formula evaluation b/w time series from different aggregations
|
||||
@@ -340,6 +343,8 @@ func (fe *FormulaEvaluator) buildSeriesLookup(timeSeriesData map[string]*TimeSer
|
||||
// when the series is returned to the caller
|
||||
// It's also used for finding matching series for a variable
|
||||
seriesMetadata: make(map[string]*TimeSeries),
|
||||
|
||||
variableToSeriesKeys: make(map[string][]string),
|
||||
}
|
||||
|
||||
for variable, aggRef := range fe.aggRefs {
|
||||
@@ -391,6 +396,7 @@ func (fe *FormulaEvaluator) buildSeriesLookup(timeSeriesData map[string]*TimeSer
|
||||
if _, exists := lookup.data[seriesKey]; !exists {
|
||||
lookup.data[seriesKey] = make(map[int64]float64, len(series.Values))
|
||||
lookup.seriesMetadata[seriesKey] = series
|
||||
lookup.variableToSeriesKeys[variable] = append(lookup.variableToSeriesKeys[variable], seriesKey)
|
||||
}
|
||||
|
||||
// Store all timestamp-value pairs
|
||||
@@ -473,35 +479,37 @@ func (fe *FormulaEvaluator) findUniqueLabelSets(lookup *seriesLookup) [][]*Label
|
||||
|
||||
// Find unique label sets using proper label comparison
|
||||
var uniqueSets [][]*Label
|
||||
var uniqueMaps []map[string]any
|
||||
for _, labelSet := range allLabelSets {
|
||||
isUnique := true
|
||||
for _, uniqueSet := range uniqueSets {
|
||||
if fe.isSubset(uniqueSet, labelSet) {
|
||||
for _, uniqueMap := range uniqueMaps {
|
||||
if isSubset(uniqueMap, labelSet) {
|
||||
isUnique = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if isUnique {
|
||||
uniqueSets = append(uniqueSets, labelSet)
|
||||
uniqueMaps = append(uniqueMaps, labelsToMap(labelSet))
|
||||
}
|
||||
}
|
||||
|
||||
return uniqueSets
|
||||
}
|
||||
|
||||
func (fe *FormulaEvaluator) isSubset(labels1, labels2 []*Label) bool {
|
||||
labelMap1 := make(map[string]any)
|
||||
labelMap2 := make(map[string]any)
|
||||
|
||||
for _, label := range labels1 {
|
||||
labelMap1[label.Key.Name] = label.Value
|
||||
}
|
||||
for _, label := range labels2 {
|
||||
labelMap2[label.Key.Name] = label.Value
|
||||
func labelsToMap(labels []*Label) map[string]any {
|
||||
m := make(map[string]any, len(labels))
|
||||
for _, label := range labels {
|
||||
m[label.Key.Name] = label.Value
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
for k, v := range labelMap2 {
|
||||
if val, ok := labelMap1[k]; !ok || val != v {
|
||||
// isSubset reports whether every label in subset is present with the same value in
|
||||
// supersetMap (i.e. subset ⊆ superset).
|
||||
func isSubset(supersetMap map[string]any, subset []*Label) bool {
|
||||
for _, label := range subset {
|
||||
if val, ok := supersetMap[label.Key.Name]; !ok || val != label.Value {
|
||||
return false
|
||||
}
|
||||
}
|
||||
@@ -517,10 +525,14 @@ func (fe *FormulaEvaluator) evaluateForLabelSet(targetLabels []*Label, lookup *s
|
||||
// for the variable
|
||||
var allTimestamps = make(map[int64]struct{})
|
||||
|
||||
// targetLabels is fixed for this call, so build its lookup once and reuse it
|
||||
// across every series comparison below.
|
||||
targetMap := labelsToMap(targetLabels)
|
||||
|
||||
for variable := range fe.aggRefs {
|
||||
// Find series with matching labels for this variable
|
||||
for seriesKey, series := range lookup.seriesMetadata {
|
||||
if strings.HasPrefix(seriesKey, variable+"|") && fe.isSubset(targetLabels, series.Labels) {
|
||||
// only this variable's series.
|
||||
for _, seriesKey := range lookup.variableToSeriesKeys[variable] {
|
||||
if isSubset(targetMap, lookup.seriesMetadata[seriesKey].Labels) {
|
||||
if timestampData, exists := lookup.data[seriesKey]; exists {
|
||||
variableData[variable] = timestampData
|
||||
// Collect all timestamps
|
||||
|
||||
Reference in New Issue
Block a user