Compare commits

..

10 Commits

Author SHA1 Message Date
Gaurav Tewari
2b53724a2c Revert "refactor(options-menu): fetch keys with the field keys hook"
This reverts commit 7cb1985c31.
2026-10-29 00:56:11 +05:30
Gaurav Tewari
7cb1985c31 refactor(options-menu): fetch keys with the field keys hook
Assisted-by: Claude Opus 5.5
2026-10-29 00:41:44 +05:30
Gaurav Tewari
2a7b9a76b9 refactor(preferences): key slices by source page only
Assisted-by: Claude Opus 5.5
2026-10-28 23:36:46 +05:30
Gaurav Tewari
79100b1f21 refactor(preferences): rename preference key to preference
Assisted-by: Claude Opus 5.5
2026-10-28 23:10:41 +05:30
Gaurav Tewari
424a66faf0 refactor(options-menu): rename data source to signal
Assisted-by: Claude Opus 5.5
2026-10-28 22:15:51 +05:30
Gaurav Tewari
bdf6987923 refactor(options-menu): branch on source page
Assisted-by: Claude Opus 5.5
2026-10-28 22:06:27 +05:30
Gaurav Tewari
2023e374a2 Merge remote-tracking branch 'origin/main' into refactor/saved-view-source-page
# Conflicts:
#	frontend/src/components/ExplorerCard/ExplorerCard.tsx
#	frontend/src/container/ExplorerOptions/ExplorerOptions.tsx
2026-10-28 21:41:46 +05:30
Gaurav Tewari
ea26449143 refactor(preferences): key preference slices by source page and panel type
Assisted-by: Claude Opus 5.5
2026-10-28 19:07:02 +05:30
Gaurav Tewari
fd8cf966cf refactor(preferences): key preference slices by saved view source
Assisted-by: Claude Opus 5.5
2026-10-28 18:53:50 +05:30
Gaurav Tewari
2225bc445d refactor(saved-views): key options menu and saved view options by source dto
Assisted-by: Claude Opus 5.5
2026-10-28 18:48:02 +05:30
49 changed files with 297 additions and 425 deletions

View File

@@ -12127,8 +12127,6 @@ components:
additionalProperties:
type: string
type: object
scope:
$ref: '#/components/schemas/SpantypesScope'
spanId:
type: string
timestamp:
@@ -12145,7 +12143,6 @@ components:
- event
- attributes
- resource
- scope
type: object
SpantypesGettableFlamegraphTrace:
properties:
@@ -12386,20 +12383,6 @@ components:
nullable: true
type: array
type: object
SpantypesScope:
properties:
attributes:
additionalProperties: {}
type: object
name:
type: string
version:
type: string
required:
- name
- version
- attributes
type: object
SpantypesSpanAggregation:
properties:
aggregation:
@@ -12769,8 +12752,6 @@ components:
type: object
response_status_code:
type: string
scope:
$ref: '#/components/schemas/SpantypesScope'
span_id:
type: string
status_code:

View File

@@ -13250,23 +13250,6 @@ export type SpantypesFlamegraphSpanDTOAttributes = { [key: string]: unknown };
export type SpantypesFlamegraphSpanDTOResource = { [key: string]: string };
export type SpantypesScopeDTOAttributes = { [key: string]: unknown };
export interface SpantypesScopeDTO {
/**
* @type object
*/
attributes: SpantypesScopeDTOAttributes;
/**
* @type string
*/
name: string;
/**
* @type string
*/
version: string;
}
export interface SpantypesFlamegraphSpanDTO {
/**
* @type object
@@ -13302,7 +13285,6 @@ export interface SpantypesFlamegraphSpanDTO {
* @type object
*/
resource: SpantypesFlamegraphSpanDTOResource;
scope: SpantypesScopeDTO;
/**
* @type string
*/
@@ -13848,7 +13830,6 @@ export interface SpantypesWaterfallSpanDTO {
* @type string
*/
response_status_code?: string;
scope?: SpantypesScopeDTO;
/**
* @type string
*/

View File

@@ -4,6 +4,7 @@ import { Button } from '@signozhq/ui/button';
import { Drawer, Tooltip } from 'antd';
import { ToggleGroup } from '@signozhq/ui/toggle-group';
import { Typography } from '@signozhq/ui/typography';
import { SavedviewtypesSourceDTO } from 'api/generated/services/sigNoz.schemas';
import LogStateIndicator from 'components/Logs/LogStateIndicator/LogStateIndicator';
import OverlayScrollbar from 'components/OverlayScrollbar/OverlayScrollbar';
import QuerySearch from 'components/QueryBuilderV2/QueryV2/QuerySearch/QuerySearch';
@@ -147,7 +148,7 @@ function LogDetailInner({
const { options } = useOptionsMenu({
storageKey: LOCALSTORAGE.LOGS_LIST_OPTIONS,
dataSource: DataSource.LOGS,
sourcePage: SavedviewtypesSourceDTO.logs,
aggregateOperator: listQuery?.aggregateOperator || StringOperators.NOOP,
});

View File

@@ -2,6 +2,7 @@ import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { useLocation } from 'react-router-dom';
import { Switch } from '@signozhq/ui/switch';
import { Typography } from '@signozhq/ui/typography';
import { SavedviewtypesSourceDTO } from 'api/generated/services/sigNoz.schemas';
import FieldsSelector from 'components/FieldsSelector';
import LogsFormatOptionsMenu from 'components/LogsFormatOptionsMenu/LogsFormatOptionsMenu';
import { MAX_LOGS_LIST_SIZE } from 'constants/liveTail';
@@ -54,7 +55,7 @@ function LiveLogsContainer({
const { options, config } = useOptionsMenu({
storageKey: LOCALSTORAGE.LOGS_LIST_OPTIONS,
dataSource: DataSource.LOGS,
sourcePage: SavedviewtypesSourceDTO.logs,
aggregateOperator: listQuery?.aggregateOperator || StringOperators.NOOP,
});

View File

@@ -5,6 +5,7 @@ import { Virtuoso, VirtuosoHandle } from 'react-virtuoso';
import { toast } from '@signozhq/ui/sonner';
import { Card } from 'antd';
import { Typography } from '@signozhq/ui/typography';
import { SavedviewtypesSourceDTO } from 'api/generated/services/sigNoz.schemas';
import LogDetail from 'components/LogDetail';
import { VIEW_TYPES } from 'components/LogDetail/constants';
import ListLogView from 'components/Logs/ListLogView';
@@ -30,7 +31,7 @@ import { useIsDarkMode } from 'hooks/useDarkMode';
import { useEventSource } from 'providers/EventSource';
// interfaces
import { ILog } from 'types/api/logs/log';
import { DataSource, StringOperators } from 'types/common/queryBuilder';
import { StringOperators } from 'types/common/queryBuilder';
import loadingPlaneUrl from '@/assets/Icons/loading-plane.gif';
import { getAbsoluteUrl } from 'utils/basePath';
@@ -68,7 +69,7 @@ function LiveLogsList({
const { options, config } = useOptionsMenu({
storageKey: LOCALSTORAGE.LOGS_LIST_OPTIONS,
dataSource: DataSource.LOGS,
sourcePage: SavedviewtypesSourceDTO.logs,
aggregateOperator: StringOperators.NOOP,
});

View File

@@ -1,6 +1,7 @@
import { useCallback, useEffect, useMemo, useState } from 'react';
import { Virtuoso } from 'react-virtuoso';
import { Button, Skeleton } from 'antd';
import { SavedviewtypesSourceDTO } from 'api/generated/services/sigNoz.schemas';
import RawLogView from 'components/Logs/RawLogView';
import OverlayScrollbar from 'components/OverlayScrollbar/OverlayScrollbar';
import { LOCALSTORAGE } from 'constants/localStorage';
@@ -16,7 +17,7 @@ import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
import useUrlQuery from 'hooks/useUrlQuery';
import { ILog } from 'types/api/logs/log';
import { Query, TagFilter } from 'types/api/queryBuilder/queryBuilderData';
import { DataSource, StringOperators } from 'types/common/queryBuilder';
import { StringOperators } from 'types/common/queryBuilder';
import { withBasePath } from 'utils/basePath';
import { useContextLogData } from './useContextLogData';
@@ -45,7 +46,7 @@ function ContextLogRenderer({
const { options } = useOptionsMenu({
storageKey: LOCALSTORAGE.LOGS_LIST_OPTIONS,
dataSource: DataSource.LOGS,
sourcePage: SavedviewtypesSourceDTO.logs,
aggregateOperator: listQuery?.aggregateOperator || StringOperators.NOOP,
});

View File

@@ -5,6 +5,7 @@ import { Virtuoso, VirtuosoHandle } from 'react-virtuoso';
import { toast } from '@signozhq/ui/sonner';
import { Card } from 'antd';
import logEvent from 'api/common/logEvent';
import { SavedviewtypesSourceDTO } from 'api/generated/services/sigNoz.schemas';
import ErrorInPlace from 'components/ErrorInPlace/ErrorInPlace';
import LogDetail from 'components/LogDetail';
import { VIEW_TYPES } from 'components/LogDetail/constants';
@@ -77,7 +78,7 @@ function LogsExplorerList({
const { options, config } = useOptionsMenu({
storageKey: LOCALSTORAGE.LOGS_LIST_OPTIONS,
dataSource: DataSource.LOGS,
sourcePage: SavedviewtypesSourceDTO.logs,
aggregateOperator:
currentStagedQueryData?.aggregateOperator || StringOperators.NOOP,
});

View File

@@ -1,6 +1,7 @@
import { ReactNode, useState } from 'react';
import { Switch } from '@signozhq/ui/switch';
import { Typography } from '@signozhq/ui/typography';
import { SavedviewtypesSourceDTO } from 'api/generated/services/sigNoz.schemas';
import DownloadOptionsMenu from 'components/DownloadOptionsMenu/DownloadOptionsMenu';
import FieldsSelector from 'components/FieldsSelector';
import LogsFormatOptionsMenu from 'components/LogsFormatOptionsMenu/LogsFormatOptionsMenu';
@@ -28,7 +29,7 @@ function LogsActionsContainer({
}): JSX.Element {
const { options, config } = useOptionsMenu({
storageKey: LOCALSTORAGE.LOGS_LIST_OPTIONS,
dataSource: DataSource.LOGS,
sourcePage: SavedviewtypesSourceDTO.logs,
aggregateOperator: listQuery?.aggregateOperator || StringOperators.NOOP,
});

View File

@@ -4,7 +4,7 @@ import { useGetQueryKeySuggestions } from 'hooks/querySuggestions/useGetQueryKey
import { useNotifications } from 'hooks/useNotifications';
import useUrlQueryData from 'hooks/useUrlQueryData';
import { usePreferenceContext } from 'providers/preferences/context/PreferenceContextProvider';
import { DataSource } from 'types/common/queryBuilder';
import { SavedviewtypesSourceDTO } from 'api/generated/services/sigNoz.schemas';
import useOptionsMenu from '../useOptionsMenu';
@@ -32,7 +32,7 @@ describe('useOptionsMenu', () => {
});
(usePreferenceContext as jest.Mock).mockReturnValue({
traces: {
[SavedviewtypesSourceDTO.traces]: {
preferences: {
columns: [],
formatting: {
@@ -44,7 +44,7 @@ describe('useOptionsMenu', () => {
updateColumns: mockUpdateColumns,
updateFormatting: mockUpdateFormatting,
},
logs: {
[SavedviewtypesSourceDTO.logs]: {
preferences: {
columns: [],
formatting: {
@@ -66,7 +66,7 @@ describe('useOptionsMenu', () => {
(useQueries as jest.Mock).mockReturnValue([]);
});
it('does not show isRoot or isEntryPoint in column options when dataSource is TRACES', () => {
it('does not show isRoot or isEntryPoint in column options when sourcePage is traces', () => {
// Mock the query key suggestions to return data including isRoot and isEntryPoint
(useGetQueryKeySuggestions as jest.Mock).mockReturnValue({
data: {
@@ -108,7 +108,7 @@ describe('useOptionsMenu', () => {
const { result } = renderHook(() =>
useOptionsMenu({
dataSource: DataSource.TRACES,
sourcePage: SavedviewtypesSourceDTO.traces,
aggregateOperator: 'count',
}),
);
@@ -127,7 +127,7 @@ describe('useOptionsMenu', () => {
expect(optionNames).toContain('serviceName');
});
it('does not show body in column options when dataSource is METRICS', () => {
it('does not show body in column options when sourcePage is metrics', () => {
// Mock the query key suggestions to return data including body
(useGetQueryKeySuggestions as jest.Mock).mockReturnValue({
data: {
@@ -163,7 +163,7 @@ describe('useOptionsMenu', () => {
const { result } = renderHook(() =>
useOptionsMenu({
dataSource: DataSource.METRICS,
sourcePage: SavedviewtypesSourceDTO.metrics,
aggregateOperator: 'count',
}),
);
@@ -180,7 +180,7 @@ describe('useOptionsMenu', () => {
expect(optionNames).toContain('value');
});
it('does not show body in column options when dataSource is LOGS', () => {
it('does not show body in column options when sourcePage is logs', () => {
// Mock the query key suggestions to return data including body
(useGetQueryKeySuggestions as jest.Mock).mockReturnValue({
data: {
@@ -216,7 +216,7 @@ describe('useOptionsMenu', () => {
const { result } = renderHook(() =>
useOptionsMenu({
dataSource: DataSource.LOGS,
sourcePage: SavedviewtypesSourceDTO.logs,
aggregateOperator: 'count',
}),
);
@@ -275,12 +275,12 @@ describe('useOptionsMenu', () => {
isFetching: false,
});
(usePreferenceContext as jest.Mock).mockReturnValue({
traces: {
[SavedviewtypesSourceDTO.traces]: {
preferences: { columns: [], formatting: baseFormatting },
updateColumns: mockUpdateColumns,
updateFormatting: mockUpdateFormatting,
},
logs: {
[SavedviewtypesSourceDTO.logs]: {
preferences: { columns: seedColumns, formatting: baseFormatting },
updateColumns: mockUpdateColumns,
updateFormatting: mockUpdateFormatting,
@@ -291,7 +291,7 @@ describe('useOptionsMenu', () => {
it('reorders by composite IDs — preserves both same-name variants distinctly', () => {
const { result } = renderHook(() =>
useOptionsMenu({
dataSource: DataSource.LOGS,
sourcePage: SavedviewtypesSourceDTO.logs,
aggregateOperator: 'count',
}),
);
@@ -322,7 +322,7 @@ describe('useOptionsMenu', () => {
it('reorder ignores ids that are not columns (e.g. state-indicator) and skips unknown ids', () => {
const { result } = renderHook(() =>
useOptionsMenu({
dataSource: DataSource.LOGS,
sourcePage: SavedviewtypesSourceDTO.logs,
aggregateOperator: 'count',
}),
);
@@ -354,7 +354,7 @@ describe('useOptionsMenu', () => {
it('removes only the variant whose composite ID matches', () => {
const { result } = renderHook(() =>
useOptionsMenu({
dataSource: DataSource.LOGS,
sourcePage: SavedviewtypesSourceDTO.logs,
aggregateOperator: 'count',
}),
);
@@ -375,7 +375,7 @@ describe('useOptionsMenu', () => {
it('removing by a non-matching composite ID is a no-op (filter returns the full list)', () => {
const { result } = renderHook(() =>
useOptionsMenu({
dataSource: DataSource.LOGS,
sourcePage: SavedviewtypesSourceDTO.logs,
aggregateOperator: 'count',
}),
);
@@ -395,7 +395,7 @@ describe('useOptionsMenu', () => {
isFetching: false,
});
(usePreferenceContext as jest.Mock).mockReturnValue({
traces: {
[SavedviewtypesSourceDTO.traces]: {
preferences: {
columns: [
{ name: 'body', fieldContext: 'log' },
@@ -407,7 +407,7 @@ describe('useOptionsMenu', () => {
updateColumns: mockUpdateColumns,
updateFormatting: mockUpdateFormatting,
},
logs: {
[SavedviewtypesSourceDTO.logs]: {
preferences: { columns: [], formatting: {} },
updateColumns: mockUpdateColumns,
updateFormatting: mockUpdateFormatting,
@@ -416,7 +416,7 @@ describe('useOptionsMenu', () => {
const { result } = renderHook(() =>
useOptionsMenu({
dataSource: DataSource.TRACES,
sourcePage: SavedviewtypesSourceDTO.traces,
aggregateOperator: 'count',
}),
);

View File

@@ -1,3 +1,4 @@
import { SavedviewtypesSourceDTO } from 'api/generated/services/sigNoz.schemas';
import { TelemetryFieldKey } from 'api/v5/v5';
import { DataSource } from 'types/common/queryBuilder';
@@ -13,6 +14,14 @@ export const defaultOptionsQuery: OptionsQuery = {
fontSize: FontSize.SMALL,
};
export const SOURCE_PAGE_SIGNAL: Record<SavedviewtypesSourceDTO, DataSource> = {
[SavedviewtypesSourceDTO.logs]: DataSource.LOGS,
[SavedviewtypesSourceDTO.traces]: DataSource.TRACES,
[SavedviewtypesSourceDTO.metrics]: DataSource.METRICS,
[SavedviewtypesSourceDTO.meter]: DataSource.METRICS,
[SavedviewtypesSourceDTO.ai_observability]: DataSource.TRACES,
};
export const EXCLUDED_COLUMNS: Record<DataSource, string[]> = {
[DataSource.TRACES]: ['body', 'isRoot', 'isEntryPoint'],
[DataSource.METRICS]: ['body'],

View File

@@ -1,6 +1,7 @@
/* eslint-disable sonarjs/cognitive-complexity */
import { useCallback, useEffect, useMemo, useState } from 'react';
import { useQueries } from 'react-query';
import { SavedviewtypesSourceDTO } from 'api/generated/services/sigNoz.schemas';
import { getKeySuggestions } from 'api/querySuggestions/getKeySuggestions';
import { TelemetryFieldKey } from 'api/v5/v5';
import { AxiosResponse } from 'axios';
@@ -10,7 +11,8 @@ import { useNotifications } from 'hooks/useNotifications';
import useUrlQueryData from 'hooks/useUrlQueryData';
import { has } from 'lodash-es';
import { AllTraceFilterKeyValue } from 'constants/traceFilterKeys';
import { usePreferenceContext } from 'providers/preferences/context/PreferenceContextProvider';
import { usePreferenceSlice } from 'providers/preferences/context/usePreferenceSlice';
import { toPreferenceSourcePage } from 'providers/preferences/utils';
import {
QueryKeyRequestProps,
QueryKeySuggestionsResponseProps,
@@ -20,13 +22,13 @@ import {
FieldDataType,
SignalType,
} from 'types/api/v5/queryRange';
import { DataSource } from 'types/common/queryBuilder';
import {
defaultLogsSelectedColumns,
defaultOptionsQuery,
defaultTraceSelectedColumns,
EXCLUDED_COLUMNS,
SOURCE_PAGE_SIGNAL,
URL_OPTIONS,
} from './constants';
import {
@@ -40,7 +42,7 @@ import { buildCompositeKey, getOptionsFromKeys } from './utils';
interface UseOptionsMenuProps {
storageKey?: string;
dataSource: DataSource;
sourcePage: SavedviewtypesSourceDTO;
aggregateOperator?: string;
initialOptions?: InitialOptions;
}
@@ -52,15 +54,16 @@ interface UseOptionsMenu {
}
const useOptionsMenu = ({
dataSource,
sourcePage,
// aggregateOperator,
initialOptions = {},
}: UseOptionsMenuProps): UseOptionsMenu => {
const signal = SOURCE_PAGE_SIGNAL[sourcePage];
const { notifications } = useNotifications();
const prefCtx = usePreferenceContext();
// TODO: send null to updateColumns and updateFormatting if dataSource is not logs or traces
const slice = dataSource === DataSource.TRACES ? prefCtx.traces : prefCtx.logs;
const { preferences, updateColumns, updateFormatting } = slice;
const { preferences, updateColumns, updateFormatting } = usePreferenceSlice(
toPreferenceSourcePage(sourcePage),
);
const [searchText, setSearchText] = useState<string>('');
const [isFocused, setIsFocused] = useState<boolean>(false);
@@ -68,10 +71,10 @@ const useOptionsMenu = ({
const initialQueryParamsV5: QueryKeyRequestProps = useMemo(
() => ({
signal: dataSource,
signal,
searchText: '',
}),
[dataSource],
[signal],
);
const { query: optionsQuery, redirectWithQuery: redirectWithOptionsData } =
@@ -127,7 +130,7 @@ const useOptionsMenu = ({
?.map((column) => attributesData.find(({ name }) => name === column))
.filter((e) => !!e) || []) as TelemetryFieldKey[];
if (dataSource === DataSource.TRACES) {
if (sourcePage === SavedviewtypesSourceDTO.traces) {
initialSelected = initialSelected
?.map((col) => {
if (col && Object.keys(AllTraceFilterKeyValue).includes(col?.name)) {
@@ -157,7 +160,7 @@ const useOptionsMenu = ({
isFetchedInitialAttributes,
initialOptions?.selectColumns,
initialAttributesResult,
dataSource,
sourcePage,
]);
const {
@@ -195,7 +198,7 @@ const useOptionsMenu = ({
fieldDataType: e.fieldDataType,
}));
}
if (dataSource === DataSource.TRACES) {
if (sourcePage === SavedviewtypesSourceDTO.traces) {
return defaultTraceSelectedColumns.map((e) => ({
...e,
name: e.name,
@@ -203,16 +206,16 @@ const useOptionsMenu = ({
}
return [];
}, [dataSource, searchedAttributesDataV5?.data.data.keys]);
}, [sourcePage, searchedAttributesDataV5?.data.data.keys]);
const initialOptionsQuery: OptionsQuery = useMemo(() => {
let defaultColumns: TelemetryFieldKey[] = defaultOptionsQuery.selectColumns;
if (dataSource === DataSource.TRACES) {
if (sourcePage === SavedviewtypesSourceDTO.traces) {
defaultColumns = defaultTraceSelectedColumns.map((e) => ({
...e,
name: e.name,
}));
} else if (dataSource === DataSource.LOGS) {
} else if (sourcePage === SavedviewtypesSourceDTO.logs) {
// eslint-disable-next-line sonarjs/no-identical-functions
defaultColumns = defaultLogsSelectedColumns.map((e) => ({
...e,
@@ -232,7 +235,7 @@ const useOptionsMenu = ({
...initialOptions,
selectColumns: finalSelectColumns,
};
}, [dataSource, initialOptions, initialSelectedColumns]);
}, [sourcePage, initialOptions, initialSelectedColumns]);
const selectedColumnKeys = useMemo(
() => preferences?.columns?.map(({ name }) => name) || [],
@@ -241,7 +244,7 @@ const useOptionsMenu = ({
const optionsFromAttributeKeys = useMemo(() => {
const filteredAttributeKeys = searchedAttributeKeys.filter((item) => {
const exclusions = EXCLUDED_COLUMNS[dataSource];
const exclusions = EXCLUDED_COLUMNS[signal];
if (exclusions) {
return !exclusions.includes(item.name);
}
@@ -249,7 +252,7 @@ const useOptionsMenu = ({
});
return getOptionsFromKeys(filteredAttributeKeys, selectedColumnKeys);
}, [dataSource, searchedAttributeKeys, selectedColumnKeys]);
}, [signal, searchedAttributeKeys, selectedColumnKeys]);
const handleRedirectWithOptionsData = useCallback(
(newQueryData: OptionsQuery) => {
@@ -285,7 +288,10 @@ const useOptionsMenu = ({
buildCompositeKey(f.name, f.fieldContext, f.fieldDataType) !== columnKey,
);
if (!newSelectedColumns?.length && dataSource !== DataSource.LOGS) {
if (
!newSelectedColumns?.length &&
sourcePage !== SavedviewtypesSourceDTO.logs
) {
notifications.error({
message: 'There must be at least one selected column',
});
@@ -294,7 +300,7 @@ const useOptionsMenu = ({
updateColumns(newSelectedColumns || []);
},
[dataSource, notifications, preferences, updateColumns],
[sourcePage, notifications, preferences, updateColumns],
);
const handleFormatChange = useCallback(

View File

@@ -4,7 +4,7 @@ import userEvent from '@testing-library/user-event';
import { Button } from '@signozhq/ui/button';
import { SavedviewtypesSourceDTO } from 'api/generated/services/sigNoz.schemas';
import ROUTES from 'constants/routes';
import { usePreferenceContext } from 'providers/preferences/context/PreferenceContextProvider';
import { usePreferenceSlice } from 'providers/preferences/context/usePreferenceSlice';
import SavedViewsHeader from '../SavedViewsHeader';
import { mockSavedViewsApi } from './savedViewsApiMock';
@@ -28,7 +28,7 @@ const errors = {
};
function Harness(): JSX.Element {
const { traces } = usePreferenceContext();
const traces = usePreferenceSlice(SavedviewtypesSourceDTO.traces);
const [headerKey, setHeaderKey] = useState(0);
const columns = traces.preferences?.columns ?? [];

View File

@@ -9,19 +9,16 @@ import {
buildSavedViewDeletePermission,
buildSavedViewUpdatePermission,
} from 'lib/authz/hooks/useAuthZ/permissions/saved-view.permissions';
import { DataSource } from 'types/common/queryBuilder';
import { SavedViewRowActionConfig, SaveViewModalMode } from './types';
export const SAVED_VIEW_SCHEMA_VERSION = SavedviewtypesSchemaVersionDTO.v2;
// Only the logs and traces explorers have columns and formatting to save.
export const SAVED_VIEW_OPTIONS_DATA_SOURCE: Partial<
Record<SavedviewtypesSourceDTO, DataSource>
> = {
[SavedviewtypesSourceDTO.logs]: DataSource.LOGS,
[SavedviewtypesSourceDTO.traces]: DataSource.TRACES,
};
export const SAVED_VIEW_OPTIONS_SOURCES: SavedviewtypesSourceDTO[] = [
SavedviewtypesSourceDTO.logs,
SavedviewtypesSourceDTO.traces,
];
// What selecting a view writes to the url, so clearing it removes exactly these.
export const SAVED_VIEW_URL_PARAMS = [

View File

@@ -8,10 +8,9 @@ import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
import { useGetSavedViewParams } from 'hooks/saveViews/useGetSavedViewParams';
import { buildSavedViewReadPermission } from 'lib/authz/hooks/useAuthZ/permissions/saved-view.permissions';
import { useAuthZ } from 'lib/authz/hooks/useAuthZ/useAuthZ';
import { DataSource } from 'types/common/queryBuilder';
import { toAPIError } from 'utils/errorUtils';
import { SAVED_VIEW_OPTIONS_DATA_SOURCE } from '../constants';
import { SAVED_VIEW_OPTIONS_SOURCES } from '../constants';
import { UseActiveSavedViewResult } from '../types';
import { hasUnsavedViewChanges } from '../utils/hasUnsavedViewChanges';
@@ -21,10 +20,8 @@ export function useActiveSavedView(
const { viewKey } = useGetSavedViewParams();
const { stagedQuery, panelType } = useQueryBuilder();
const optionsDataSource = SAVED_VIEW_OPTIONS_DATA_SOURCE[source];
const { options } = useOptionsMenu({
dataSource: optionsDataSource ?? DataSource.LOGS,
});
const hasOptions = SAVED_VIEW_OPTIONS_SOURCES.includes(source);
const { options } = useOptionsMenu({ sourcePage: source });
const { isLoading: isCheckingRead, deniedPermissions } = useAuthZ(
[buildSavedViewReadPermission(viewKey)],
@@ -63,7 +60,8 @@ export function useActiveSavedView(
view,
stagedQuery,
panelType: panelType ?? PANEL_TYPES.LIST,
options: optionsDataSource ? options : undefined,
options: hasOptions ? options : undefined,
sourcePage: source,
});
}, [
view,
@@ -71,7 +69,8 @@ export function useActiveSavedView(
isStagedQueryCurrent,
stagedQuery,
panelType,
optionsDataSource,
hasOptions,
source,
options,
]);

View File

@@ -20,13 +20,13 @@ import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
import { useGetSavedViewParams } from 'hooks/saveViews/useGetSavedViewParams';
import { useHandleExplorerTabChange } from 'hooks/useHandleExplorerTabChange';
import { useSafeNavigate } from 'hooks/useSafeNavigate';
import { usePreferenceContext } from 'providers/preferences/context/PreferenceContextProvider';
import { DataSource } from 'types/common/queryBuilder';
import { usePreferenceSlice } from 'providers/preferences/context/usePreferenceSlice';
import { toPreferenceSourcePage } from 'providers/preferences/utils';
import { toAPIError } from 'utils/errorUtils';
import { getUnstableCurrentSearchParams } from 'utils/getUnstableCurrentSearchParams';
import {
SAVED_VIEW_OPTIONS_DATA_SOURCE,
SAVED_VIEW_OPTIONS_SOURCES,
SAVED_VIEW_SCHEMA_VERSION,
SAVED_VIEW_TOAST_POSITION,
SAVED_VIEW_URL_PARAMS,
@@ -60,15 +60,9 @@ export function useSavedViewActions(
const { viewKey } = useGetSavedViewParams();
const logSavedViewEvent = useSavedViewLogEvent(source);
const optionsDataSource = SAVED_VIEW_OPTIONS_DATA_SOURCE[source];
const { options } = useOptionsMenu({
dataSource: optionsDataSource ?? DataSource.LOGS,
});
const preferences = usePreferenceContext();
const preferencesSlice =
optionsDataSource === DataSource.TRACES
? preferences.traces
: preferences.logs;
const hasOptions = SAVED_VIEW_OPTIONS_SOURCES.includes(source);
const { options } = useOptionsMenu({ sourcePage: source });
const preferencesSlice = usePreferenceSlice(toPreferenceSourcePage(source));
const { mutateAsync: createSavedView, isLoading: isCreating } =
useCreateSavedView();
@@ -86,10 +80,10 @@ export function useSavedViewActions(
query: stagedQuery ?? currentQuery,
panelType: panelType ?? PANEL_TYPES.LIST,
displayName,
options: optionsDataSource ? options : undefined,
options: hasOptions ? options : undefined,
color,
}),
[stagedQuery, currentQuery, panelType, optionsDataSource, options],
[stagedQuery, currentQuery, panelType, hasOptions, options],
);
const selectView = useCallback(
@@ -105,10 +99,10 @@ export function useSavedViewActions(
const revertView = useCallback(
(view: SavedviewtypesSavedViewDTO): void => {
if (optionsDataSource) {
if (hasOptions) {
const { columns, formatting } = getViewColumnsAndFormatting(
view.spec,
optionsDataSource,
source,
);
preferencesSlice.updateColumns(columns);
if (formatting) {
@@ -120,7 +114,7 @@ export function useSavedViewActions(
[SavedViewEventKeys.ViewId]: view.id,
});
},
[optionsDataSource, preferencesSlice, selectView, logSavedViewEvent],
[hasOptions, source, preferencesSlice, selectView, logSavedViewEvent],
);
// The query builder redirects only ever add params, so the url is rebuilt

View File

@@ -32,6 +32,7 @@ export interface HasUnsavedViewChangesArgs {
stagedQuery: Query | null;
panelType: PANEL_TYPES;
options?: SavedViewOptions;
sourcePage: SavedviewtypesSourceDTO;
}
export interface UseLastUsedViewResult {

View File

@@ -1,6 +1,7 @@
import {
SavedviewtypesSavedViewDTO,
SavedviewtypesSavedViewSpecDTO,
SavedviewtypesSourceDTO,
} from 'api/generated/services/sigNoz.schemas';
import {
initialQueriesMap,
@@ -120,6 +121,7 @@ describe('hasUnsavedViewChanges', () => {
view,
stagedQuery: staged,
panelType: PANEL_TYPES.LIST,
sourcePage: SavedviewtypesSourceDTO.traces,
options: { ...options, selectColumns: [columns[0]] },
}),
).toBe(true);
@@ -139,6 +141,7 @@ describe('hasUnsavedViewChanges', () => {
view: bare,
stagedQuery: stagedFrom(bare, DataSource.TRACES),
panelType: PANEL_TYPES.LIST,
sourcePage: SavedviewtypesSourceDTO.traces,
options: { ...options, selectColumns: defaultTraceSelectedColumns },
}),
).toBe(false);
@@ -163,6 +166,7 @@ describe('hasUnsavedViewChanges', () => {
view: logsView,
stagedQuery: logsStaged,
panelType: PANEL_TYPES.LIST,
sourcePage: SavedviewtypesSourceDTO.logs,
options: { selectColumns: shownColumns, ...formatting },
}),
).toBe(false);
@@ -174,6 +178,7 @@ describe('hasUnsavedViewChanges', () => {
view: logsView,
stagedQuery: logsStaged,
panelType: PANEL_TYPES.LIST,
sourcePage: SavedviewtypesSourceDTO.logs,
options: { selectColumns: shownColumns, ...formatting, maxLines: 5 },
}),
).toBe(true);
@@ -193,6 +198,7 @@ describe('hasUnsavedViewChanges', () => {
view: bare,
stagedQuery: stagedFrom(bare, DataSource.LOGS),
panelType: PANEL_TYPES.LIST,
sourcePage: SavedviewtypesSourceDTO.logs,
options: {
selectColumns: defaultLogsSelectedColumns,
format: 'table',
@@ -236,6 +242,7 @@ describe('hasUnsavedViewChanges', () => {
view: olderView,
stagedQuery: stagedFrom(olderView, DataSource.METRICS),
panelType: PANEL_TYPES.TIME_SERIES,
sourcePage: SavedviewtypesSourceDTO.metrics,
}),
).toBe(false);
});
@@ -283,6 +290,7 @@ describe('hasUnsavedViewChanges', () => {
view: storedView,
stagedQuery: stagedWithTag,
panelType: PANEL_TYPES.TIME_SERIES,
sourcePage: SavedviewtypesSourceDTO.metrics,
}),
).toBe(false);
});
@@ -322,6 +330,7 @@ describe('hasUnsavedViewChanges', () => {
view: noOrder,
stagedQuery: stagedFrom(noOrder, DataSource.LOGS),
panelType: PANEL_TYPES.LIST,
sourcePage: SavedviewtypesSourceDTO.logs,
}),
).toBe(false);
});
@@ -337,6 +346,7 @@ describe('hasUnsavedViewChanges', () => {
view: noOrder,
stagedQuery: sorted,
panelType: PANEL_TYPES.LIST,
sourcePage: SavedviewtypesSourceDTO.logs,
}),
).toBe(true);
@@ -346,6 +356,7 @@ describe('hasUnsavedViewChanges', () => {
view: saved,
stagedQuery: stagedFrom(saved, DataSource.LOGS),
panelType: PANEL_TYPES.LIST,
sourcePage: SavedviewtypesSourceDTO.logs,
}),
).toBe(false);
});
@@ -362,6 +373,7 @@ describe('hasUnsavedViewChanges', () => {
view: legacy,
stagedQuery: stagedFrom(legacy, DataSource.LOGS),
panelType: PANEL_TYPES.LIST,
sourcePage: SavedviewtypesSourceDTO.logs,
}),
).toBe(false);
});
@@ -373,6 +385,7 @@ describe('hasUnsavedViewChanges', () => {
view,
stagedQuery: staged,
panelType: PANEL_TYPES.LIST,
sourcePage: SavedviewtypesSourceDTO.traces,
options: { ...options, ...formatting, maxLines: 5 },
}),
).toBe(false);

View File

@@ -1,4 +1,7 @@
import { SavedviewtypesSavedViewSpecDTO } from 'api/generated/services/sigNoz.schemas';
import {
SavedviewtypesSavedViewSpecDTO,
SavedviewtypesSourceDTO,
} from 'api/generated/services/sigNoz.schemas';
import { TelemetryFieldKey } from 'api/v5/v5';
import {
defaultLogsSelectedColumns,
@@ -7,7 +10,6 @@ import {
} from 'container/OptionsMenu/constants';
import { FontSize, LogViewMode } from 'container/OptionsMenu/types';
import { Preferences } from 'providers/preferences/types';
import { DataSource } from 'types/common/queryBuilder';
function withColumnNames(
columns: TelemetryFieldKey[] | undefined,
@@ -25,11 +27,11 @@ function withColumnNames(
// defaults filled in where the view has none.
export function getViewColumnsAndFormatting(
spec: SavedviewtypesSavedViewSpecDTO | undefined,
dataSource: DataSource,
sourcePage: SavedviewtypesSourceDTO,
): Preferences {
const selectedFields = spec?.selectedFields as TelemetryFieldKey[] | undefined;
if (dataSource === DataSource.LOGS) {
if (sourcePage === SavedviewtypesSourceDTO.logs) {
return {
columns: ensureLogsRequiredColumns(
withColumnNames(selectedFields) || defaultLogsSelectedColumns,
@@ -42,7 +44,7 @@ export function getViewColumnsAndFormatting(
},
};
}
if (dataSource === DataSource.TRACES) {
if (sourcePage === SavedviewtypesSourceDTO.traces) {
return { columns: selectedFields || defaultTraceSelectedColumns };
}
return { columns: [] };

View File

@@ -1,7 +1,9 @@
import { TelemetrytypesFieldContextDTO } from 'api/generated/services/sigNoz.schemas';
import {
SavedviewtypesSourceDTO,
TelemetrytypesFieldContextDTO,
} from 'api/generated/services/sigNoz.schemas';
import { mapCompositeQueryFromQuery } from 'lib/newQueryBuilder/queryBuilderMappers/mapCompositeQueryFromQuery';
import isEqual from 'lodash-es/isEqual';
import { DataSource } from 'types/common/queryBuilder';
import { HasUnsavedViewChangesArgs } from '../types';
import { getSavedViewQuery } from './getSavedViewQuery';
@@ -50,6 +52,7 @@ export function hasUnsavedViewChanges({
stagedQuery,
panelType,
options,
sourcePage,
}: HasUnsavedViewChangesArgs): boolean {
if (!stagedQuery) {
return false;
@@ -76,10 +79,9 @@ export function hasUnsavedViewChanges({
return false;
}
const dataSource = stagedQuery.builder.queryData[0]?.dataSource;
const { columns, formatting } = getViewColumnsAndFormatting(
view.spec,
dataSource,
sourcePage,
);
const toNames = (fields: { name?: string; key?: string }[]): unknown[] =>
fields.map((field) => field.name ?? field.key);
@@ -87,7 +89,7 @@ export function hasUnsavedViewChanges({
return true;
}
if (dataSource !== DataSource.LOGS || !formatting) {
if (sourcePage !== SavedviewtypesSourceDTO.logs || !formatting) {
return false;
}
return (

View File

@@ -11,6 +11,7 @@ import {
// eslint-disable-next-line no-restricted-imports
import { useSelector } from 'react-redux';
import logEvent from 'api/common/logEvent';
import { SavedviewtypesSourceDTO } from 'api/generated/services/sigNoz.schemas';
import DownloadOptionsMenu from 'components/DownloadOptionsMenu/DownloadOptionsMenu';
import ListViewOrderBy from 'components/OrderBy/ListViewOrderBy';
import type { TableColumnDef } from 'components/TanStackTableView/types';
@@ -86,7 +87,7 @@ function ListView({
const { options, config } = useOptionsMenu({
storageKey: LOCALSTORAGE.TRACES_LIST_OPTIONS,
dataSource: DataSource.TRACES,
sourcePage: SavedviewtypesSourceDTO.traces,
aggregateOperator: 'count',
initialOptions: {
selectColumns: defaultSelectedColumns,

View File

@@ -73,7 +73,7 @@ function TracesExplorer(): JSX.Element {
const { options } = useOptionsMenu({
storageKey: LOCALSTORAGE.TRACES_LIST_OPTIONS,
dataSource: DataSource.TRACES,
sourcePage: SavedviewtypesSourceDTO.traces,
aggregateOperator: 'noop',
initialOptions: {
selectColumns: defaultSelectedColumns,

View File

@@ -1,15 +1,15 @@
import { SavedviewtypesSourceDTO } from 'api/generated/services/sigNoz.schemas';
import { TelemetryFieldKey } from 'api/v5/v5';
import { LOCALSTORAGE } from 'constants/localStorage';
import { LogViewMode } from 'container/OptionsMenu/types';
import { LogViewMode, FontSize } from 'container/OptionsMenu/types';
import {
defaultLogsSelectedColumns,
defaultTraceSelectedColumns,
} from 'container/OptionsMenu/constants';
import { FontSize } from 'container/OptionsMenu/types';
import { render, screen, userEvent } from 'tests/test-utils';
import { DataSource } from 'types/common/queryBuilder';
import { usePreferenceContext } from '../context/PreferenceContextProvider';
import { usePreferenceSlice } from '../context/usePreferenceSlice';
import { PreferenceSourcePage } from '../types';
const ROUTE_LOGS = '/logs';
const ROUTE_TRACES = '/traces';
@@ -38,14 +38,13 @@ function getLocalStorageJSON<T>(key: string): T | null {
}
function Consumer({
dataSource,
sourcePage,
testIdPrefix,
}: {
dataSource: DataSource;
sourcePage: PreferenceSourcePage;
testIdPrefix: string;
}): JSX.Element {
const ctx = usePreferenceContext();
const slice = dataSource === DataSource.TRACES ? ctx.traces : ctx.logs;
const slice = usePreferenceSlice(sourcePage);
return (
<div>
@@ -58,7 +57,7 @@ function Consumer({
type="button"
onClick={(): void => {
const newCols: TelemetryFieldKey[] =
dataSource === DataSource.TRACES
sourcePage === SavedviewtypesSourceDTO.traces
? (defaultTraceSelectedColumns.slice(0, 1) as TelemetryFieldKey[])
: (defaultLogsSelectedColumns.slice(0, 1) as TelemetryFieldKey[]);
slice.updateColumns(newCols);
@@ -78,7 +77,10 @@ describe('PreferencesProvider integration', () => {
describe('Logs', () => {
it('loads defaults when no localStorage or url provided', () => {
render(
<Consumer dataSource={DataSource.LOGS} testIdPrefix={TESTID_LOGS} />,
<Consumer
sourcePage={SavedviewtypesSourceDTO.logs}
testIdPrefix={TESTID_LOGS}
/>,
undefined,
{
initialRoute: ROUTE_LOGS,
@@ -101,7 +103,10 @@ describe('PreferencesProvider integration', () => {
});
render(
<Consumer dataSource={DataSource.LOGS} testIdPrefix={TESTID_LOGS} />,
<Consumer
sourcePage={SavedviewtypesSourceDTO.logs}
testIdPrefix={TESTID_LOGS}
/>,
undefined,
{
initialRoute: ROUTE_LOGS,
@@ -116,7 +121,10 @@ describe('PreferencesProvider integration', () => {
it('direct mode updateColumns persists to localStorage', async () => {
const user = userEvent.setup({ pointerEventsCheck: 0 });
render(
<Consumer dataSource={DataSource.LOGS} testIdPrefix={TESTID_LOGS} />,
<Consumer
sourcePage={SavedviewtypesSourceDTO.logs}
testIdPrefix={TESTID_LOGS}
/>,
undefined,
{
initialRoute: ROUTE_LOGS,
@@ -142,7 +150,7 @@ describe('PreferencesProvider integration', () => {
const user = userEvent.setup({ pointerEventsCheck: 0 });
render(
<Consumer dataSource={DataSource.LOGS} testIdPrefix="logs" />,
<Consumer sourcePage={SavedviewtypesSourceDTO.logs} testIdPrefix="logs" />,
undefined,
{
initialRoute: initialEntry,
@@ -175,7 +183,10 @@ describe('PreferencesProvider integration', () => {
});
render(
<Consumer dataSource={DataSource.LOGS} testIdPrefix={TESTID_LOGS} />,
<Consumer
sourcePage={SavedviewtypesSourceDTO.logs}
testIdPrefix={TESTID_LOGS}
/>,
undefined,
{
initialRoute: ROUTE_LOGS,
@@ -197,7 +208,7 @@ describe('PreferencesProvider integration', () => {
const user = userEvent.setup({ pointerEventsCheck: 0 });
function FormattingConsumer(): JSX.Element {
const { logs } = usePreferenceContext();
const logs = usePreferenceSlice(SavedviewtypesSourceDTO.logs);
return (
<button
data-testid="logs-update-formatting"
@@ -235,7 +246,10 @@ describe('PreferencesProvider integration', () => {
const initialEntry = `/logs?viewKey=${encodeURIComponent(viewKey)}`;
render(
<Consumer dataSource={DataSource.LOGS} testIdPrefix={TESTID_LOGS} />,
<Consumer
sourcePage={SavedviewtypesSourceDTO.logs}
testIdPrefix={TESTID_LOGS}
/>,
undefined,
{ initialRoute: initialEntry },
);
@@ -254,7 +268,10 @@ describe('PreferencesProvider integration', () => {
describe('Traces', () => {
it('loads defaults when no localStorage or url provided', () => {
render(
<Consumer dataSource={DataSource.TRACES} testIdPrefix={TESTID_TRACES} />,
<Consumer
sourcePage={SavedviewtypesSourceDTO.traces}
testIdPrefix={TESTID_TRACES}
/>,
undefined,
{
initialRoute: ROUTE_TRACES,
@@ -273,7 +290,10 @@ describe('PreferencesProvider integration', () => {
});
render(
<Consumer dataSource={DataSource.TRACES} testIdPrefix={TESTID_TRACES} />,
<Consumer
sourcePage={SavedviewtypesSourceDTO.traces}
testIdPrefix={TESTID_TRACES}
/>,
undefined,
{
initialRoute: ROUTE_TRACES,
@@ -286,7 +306,10 @@ describe('PreferencesProvider integration', () => {
it('direct mode updateColumns persists to localStorage', async () => {
const user = userEvent.setup({ pointerEventsCheck: 0 });
render(
<Consumer dataSource={DataSource.TRACES} testIdPrefix={TESTID_TRACES} />,
<Consumer
sourcePage={SavedviewtypesSourceDTO.traces}
testIdPrefix={TESTID_TRACES}
/>,
undefined,
{
initialRoute: ROUTE_TRACES,
@@ -309,7 +332,10 @@ describe('PreferencesProvider integration', () => {
const user = userEvent.setup({ pointerEventsCheck: 0 });
render(
<Consumer dataSource={DataSource.TRACES} testIdPrefix="traces" />,
<Consumer
sourcePage={SavedviewtypesSourceDTO.traces}
testIdPrefix="traces"
/>,
undefined,
{
initialRoute: initialEntry,
@@ -338,7 +364,10 @@ describe('PreferencesProvider integration', () => {
});
render(
<Consumer dataSource={DataSource.TRACES} testIdPrefix={TESTID_TRACES} />,
<Consumer
sourcePage={SavedviewtypesSourceDTO.traces}
testIdPrefix={TESTID_TRACES}
/>,
undefined,
{ initialRoute: ROUTE_TRACES },
);
@@ -355,7 +384,7 @@ describe('PreferencesProvider integration', () => {
const user = userEvent.setup({ pointerEventsCheck: 0 });
function TracesFormattingConsumer(): JSX.Element {
const { traces } = usePreferenceContext();
const traces = usePreferenceSlice(SavedviewtypesSourceDTO.traces);
return (
<button
data-testid="traces-update-formatting"
@@ -390,7 +419,10 @@ describe('PreferencesProvider integration', () => {
const initialEntry = `/traces?viewKey=${encodeURIComponent(viewKey)}`;
render(
<Consumer dataSource={DataSource.TRACES} testIdPrefix={TESTID_TRACES} />,
<Consumer
sourcePage={SavedviewtypesSourceDTO.traces}
testIdPrefix={TESTID_TRACES}
/>,
undefined,
{ initialRoute: initialEntry },
);

View File

@@ -1,6 +1,6 @@
import { SavedviewtypesSourceDTO } from 'api/generated/services/sigNoz.schemas';
import { renderHook, waitFor } from '@testing-library/react';
import { defaultLogsSelectedColumns } from 'container/OptionsMenu/constants';
import { DataSource } from 'types/common/queryBuilder';
import logsLoaderConfig from '../configs/logsLoaderConfig';
import { usePreferenceLoader } from '../loader/usePreferenceLoader';
@@ -50,7 +50,7 @@ describe('usePreferenceLoader', () => {
const setReSync = jest.fn();
const { result } = renderHook(() =>
usePreferenceLoader({
dataSource: DataSource.LOGS,
sourcePage: SavedviewtypesSourceDTO.logs,
reSync: false,
setReSync,
}),
@@ -74,7 +74,7 @@ describe('usePreferenceLoader', () => {
const setReSync = jest.fn();
const { result } = renderHook(() =>
usePreferenceLoader({
dataSource: DataSource.TRACES,
sourcePage: SavedviewtypesSourceDTO.traces,
reSync: false,
setReSync,
}),
@@ -99,7 +99,7 @@ describe('usePreferenceLoader', () => {
// We'll unmount quickly to avoid the infinite loop
const { unmount } = renderHook(() =>
usePreferenceLoader({
dataSource: DataSource.LOGS,
sourcePage: SavedviewtypesSourceDTO.logs,
reSync: true,
setReSync,
}),
@@ -130,7 +130,7 @@ describe('usePreferenceLoader', () => {
const setReSync = jest.fn();
const { result } = renderHook(() =>
usePreferenceLoader({
dataSource: DataSource.LOGS,
sourcePage: SavedviewtypesSourceDTO.logs,
reSync: true,
setReSync,
}),

View File

@@ -9,7 +9,6 @@ import {
defaultTraceSelectedColumns,
ensureLogsRequiredColumns,
} from 'container/OptionsMenu/constants';
import { DataSource } from 'types/common/queryBuilder';
import { usePreferenceSync } from '../sync/usePreferenceSync';
import { PreferenceMode } from '../types';
@@ -84,7 +83,7 @@ describe('usePreferenceSync in saved view mode', () => {
renderHook(() =>
usePreferenceSync({
mode: PreferenceMode.DIRECT,
dataSource: DataSource.LOGS,
sourcePage: SavedviewtypesSourceDTO.logs,
savedViewId: undefined,
}),
);
@@ -101,7 +100,7 @@ describe('usePreferenceSync in saved view mode', () => {
const { result } = renderHook(() =>
usePreferenceSync({
mode: PreferenceMode.DIRECT,
dataSource: DataSource.LOGS,
sourcePage: SavedviewtypesSourceDTO.logs,
savedViewId: undefined,
}),
);
@@ -120,7 +119,7 @@ describe('usePreferenceSync in saved view mode', () => {
const { result } = renderHook(() =>
usePreferenceSync({
mode: PreferenceMode.SAVED_VIEW,
dataSource: DataSource.LOGS,
sourcePage: SavedviewtypesSourceDTO.logs,
savedViewId: 'view-1',
}),
);
@@ -147,7 +146,7 @@ describe('usePreferenceSync in saved view mode', () => {
const { result } = renderHook(() =>
usePreferenceSync({
mode: PreferenceMode.SAVED_VIEW,
dataSource: DataSource.LOGS,
sourcePage: SavedviewtypesSourceDTO.logs,
savedViewId: 'view-1',
}),
);
@@ -174,14 +173,14 @@ describe('usePreferenceSync in saved view mode', () => {
const withFields = renderHook(() =>
usePreferenceSync({
mode: PreferenceMode.SAVED_VIEW,
dataSource: DataSource.TRACES,
sourcePage: SavedviewtypesSourceDTO.traces,
savedViewId: 'with-fields',
}),
);
const withoutFields = renderHook(() =>
usePreferenceSync({
mode: PreferenceMode.SAVED_VIEW,
dataSource: DataSource.TRACES,
sourcePage: SavedviewtypesSourceDTO.traces,
savedViewId: 'without-fields',
}),
);
@@ -202,7 +201,7 @@ describe('usePreferenceSync in saved view mode', () => {
const { result } = renderHook(() =>
usePreferenceSync({
mode: PreferenceMode.SAVED_VIEW,
dataSource: DataSource.LOGS,
sourcePage: SavedviewtypesSourceDTO.logs,
savedViewId: 'missing',
}),
);

View File

@@ -1,15 +1,14 @@
import { SavedviewtypesSourceDTO } from 'api/generated/services/sigNoz.schemas';
import { act } from 'react-dom/test-utils';
import { renderHook } from '@testing-library/react';
import { TelemetryFieldKey } from 'api/v5/v5';
import { LogViewMode } from 'container/OptionsMenu/types';
import { FontSize } from 'container/OptionsMenu/types';
import { LogViewMode, FontSize } from 'container/OptionsMenu/types';
import {
FormattingOptions,
PreferenceMode,
Preferences,
} from 'providers/preferences/types';
import { DataTypes } from 'types/api/queryBuilder/queryAutocompleteResponse';
import { DataSource } from 'types/common/queryBuilder';
import { usePreferenceUpdater } from '../updater/usePreferenceUpdater';
@@ -62,7 +61,7 @@ describe('usePreferenceUpdater', () => {
const { result } = renderHook(() =>
usePreferenceUpdater({
dataSource: DataSource.LOGS,
sourcePage: SavedviewtypesSourceDTO.logs,
mode: PreferenceMode.DIRECT,
preferences: mockPreferences,
setReSync,
@@ -75,7 +74,7 @@ describe('usePreferenceUpdater', () => {
expect(typeof result.current.updateFormatting).toBe('function');
});
it('should call the logs updater for updateColumns with logs dataSource', () => {
it('should call the logs updater for updateColumns with the logs list key', () => {
const setReSync = jest.fn();
const setSavedViewPreferences = jest.fn();
const newColumns: TelemetryFieldKey[] = [
@@ -88,7 +87,7 @@ describe('usePreferenceUpdater', () => {
const { result } = renderHook(() =>
usePreferenceUpdater({
dataSource: DataSource.LOGS,
sourcePage: SavedviewtypesSourceDTO.logs,
mode: PreferenceMode.DIRECT,
preferences: mockPreferences,
setReSync,
@@ -108,7 +107,7 @@ describe('usePreferenceUpdater', () => {
expect(setReSync).toHaveBeenCalledWith(true);
});
it('should call the logs updater for updateFormatting with logs dataSource', () => {
it('should call the logs updater for updateFormatting with the logs list key', () => {
const setReSync = jest.fn();
const setSavedViewPreferences = jest.fn();
const newFormatting: FormattingOptions = {
@@ -120,7 +119,7 @@ describe('usePreferenceUpdater', () => {
const { result } = renderHook(() =>
usePreferenceUpdater({
dataSource: DataSource.LOGS,
sourcePage: SavedviewtypesSourceDTO.logs,
mode: PreferenceMode.DIRECT,
preferences: mockPreferences,
setReSync,
@@ -140,7 +139,7 @@ describe('usePreferenceUpdater', () => {
expect(setReSync).toHaveBeenCalledWith(true);
});
it('should call the traces updater for updateColumns with traces dataSource', () => {
it('should call the traces updater for updateColumns with the traces list key', () => {
const setReSync = jest.fn();
const setSavedViewPreferences = jest.fn();
const newColumns: TelemetryFieldKey[] = [
@@ -153,7 +152,7 @@ describe('usePreferenceUpdater', () => {
const { result } = renderHook(() =>
usePreferenceUpdater({
dataSource: DataSource.TRACES,
sourcePage: SavedviewtypesSourceDTO.traces,
mode: PreferenceMode.DIRECT,
preferences: mockPreferences,
setReSync,
@@ -173,7 +172,7 @@ describe('usePreferenceUpdater', () => {
expect(setReSync).toHaveBeenCalledWith(true);
});
it('should call the traces updater for updateFormatting with traces dataSource', () => {
it('should call the traces updater for updateFormatting with the traces list key', () => {
const setReSync = jest.fn();
const setSavedViewPreferences = jest.fn();
const newFormatting: FormattingOptions = {
@@ -185,7 +184,7 @@ describe('usePreferenceUpdater', () => {
const { result } = renderHook(() =>
usePreferenceUpdater({
dataSource: DataSource.TRACES,
sourcePage: SavedviewtypesSourceDTO.traces,
mode: PreferenceMode.DIRECT,
preferences: mockPreferences,
setReSync,
@@ -211,7 +210,7 @@ describe('usePreferenceUpdater', () => {
const { result } = renderHook(() =>
usePreferenceUpdater({
dataSource: DataSource.LOGS,
sourcePage: SavedviewtypesSourceDTO.logs,
mode: PreferenceMode.DIRECT,
preferences: mockPreferences,
setReSync,

View File

@@ -5,12 +5,12 @@ import React, {
useContext,
useMemo,
} from 'react';
import { SavedviewtypesSourceDTO } from 'api/generated/services/sigNoz.schemas';
import useUrlQuery from 'hooks/useUrlQuery';
import {
PreferenceContextValue,
PreferenceMode,
} from 'providers/preferences/types';
import { DataSource } from 'types/common/queryBuilder';
import { usePreferenceSync } from '../sync/usePreferenceSync';
@@ -38,19 +38,19 @@ export function PreferenceContextProvider({
const logsSlice = usePreferenceSync({
mode: savedViewId ? PreferenceMode.SAVED_VIEW : PreferenceMode.DIRECT,
savedViewId: savedViewId || undefined,
dataSource: DataSource.LOGS,
sourcePage: SavedviewtypesSourceDTO.logs,
});
const tracesSlice = usePreferenceSync({
mode: savedViewId ? PreferenceMode.SAVED_VIEW : PreferenceMode.DIRECT,
savedViewId: savedViewId || undefined,
dataSource: DataSource.TRACES,
sourcePage: SavedviewtypesSourceDTO.traces,
});
const value = useMemo<PreferenceContextValue>(
() => ({
logs: logsSlice,
traces: tracesSlice,
[SavedviewtypesSourceDTO.logs]: logsSlice,
[SavedviewtypesSourceDTO.traces]: tracesSlice,
}),
[logsSlice, tracesSlice],
);

View File

@@ -0,0 +1,8 @@
import { PreferenceSlice, PreferenceSourcePage } from '../types';
import { usePreferenceContext } from './PreferenceContextProvider';
export function usePreferenceSlice(
sourcePage: PreferenceSourcePage,
): PreferenceSlice {
return usePreferenceContext()[sourcePage];
}

View File

@@ -1,12 +1,12 @@
import { useEffect, useState } from 'react';
import { SavedviewtypesSourceDTO } from 'api/generated/services/sigNoz.schemas';
import { TelemetryFieldKey } from 'api/v5/v5';
import { ensureLogsRequiredColumns } from 'container/OptionsMenu/constants';
import { has } from 'lodash-es';
import { DataSource } from 'types/common/queryBuilder';
import logsLoaderConfig from '../configs/logsLoaderConfig';
import tracesLoaderConfig from '../configs/tracesLoaderConfig';
import { FormattingOptions, Preferences } from '../types';
import { FormattingOptions, Preferences, PreferenceSourcePage } from '../types';
const migrateColumns = (columns: any): any =>
columns.map((column: any) => {
@@ -62,15 +62,21 @@ function logsPreferencesLoader(): {
function tracesPreferencesLoader(): {
columns: TelemetryFieldKey[];
} {
return preferencesLoader(tracesLoaderConfig);
const { columns } = preferencesLoader<Preferences>(tracesLoaderConfig);
return { columns };
}
const PREFERENCE_LOADERS: Record<PreferenceSourcePage, () => Preferences> = {
[SavedviewtypesSourceDTO.logs]: logsPreferencesLoader,
[SavedviewtypesSourceDTO.traces]: tracesPreferencesLoader,
};
export function usePreferenceLoader({
dataSource,
sourcePage,
reSync,
setReSync,
}: {
dataSource: DataSource;
sourcePage: PreferenceSourcePage;
reSync: boolean;
setReSync: (value: boolean) => void;
}): {
@@ -78,17 +84,10 @@ export function usePreferenceLoader({
loading: boolean;
error: Error | null;
} {
const [preferences, setPreferences] = useState<Preferences | null>(() => {
if (dataSource === DataSource.LOGS) {
const { columns, formatting } = logsPreferencesLoader();
return { columns, formatting };
}
if (dataSource === DataSource.TRACES) {
const { columns } = tracesPreferencesLoader();
return { columns };
}
return null;
});
const loadSlicePreferences = PREFERENCE_LOADERS[sourcePage];
const [preferences, setPreferences] = useState<Preferences | null>(
loadSlicePreferences,
);
const [error, setError] = useState<Error | null>(null);
useEffect((): void => {
@@ -96,15 +95,7 @@ export function usePreferenceLoader({
setError(null);
try {
if (dataSource === DataSource.LOGS) {
const { columns, formatting } = logsPreferencesLoader();
setPreferences({ columns, formatting });
}
if (dataSource === DataSource.TRACES) {
const { columns } = tracesPreferencesLoader();
setPreferences({ columns });
}
setPreferences(loadSlicePreferences());
} catch (e) {
setError(e as Error);
} finally {
@@ -119,7 +110,7 @@ export function usePreferenceLoader({
if (reSync) {
loadPreferences();
}
}, [dataSource, reSync, setReSync]);
}, [loadSlicePreferences, reSync, setReSync]);
return { preferences, loading: false, error };
}

View File

@@ -3,19 +3,23 @@ import { useEffect, useRef, useState } from 'react';
import { useGetSavedView } from 'api/generated/services/saved-view';
import { TelemetryFieldKey } from 'api/v5/v5';
import { getViewColumnsAndFormatting } from 'container/SavedViews/utils/getViewColumnsAndFormatting';
import { DataSource } from 'types/common/queryBuilder';
import { usePreferenceLoader } from '../loader/usePreferenceLoader';
import { FormattingOptions, PreferenceMode, Preferences } from '../types';
import {
FormattingOptions,
PreferenceMode,
Preferences,
PreferenceSourcePage,
} from '../types';
import { usePreferenceUpdater } from '../updater/usePreferenceUpdater';
export function usePreferenceSync({
mode,
dataSource,
sourcePage,
savedViewId,
}: {
mode: PreferenceMode;
dataSource: DataSource;
sourcePage: PreferenceSourcePage;
savedViewId: string | undefined;
}): {
preferences: Preferences | null;
@@ -41,25 +45,25 @@ export function usePreferenceSync({
if (view && view.id === savedViewId) {
if (seededViewId.current !== savedViewId) {
seededViewId.current = savedViewId;
setSavedViewPreferences(getViewColumnsAndFormatting(view.spec, dataSource));
setSavedViewPreferences(getViewColumnsAndFormatting(view.spec, sourcePage));
}
return;
}
seededViewId.current = undefined;
setSavedViewPreferences(getViewColumnsAndFormatting(undefined, dataSource));
}, [viewData, dataSource, savedViewId]);
setSavedViewPreferences(getViewColumnsAndFormatting(undefined, sourcePage));
}, [viewData, sourcePage, savedViewId]);
// We are using a reSync state because we have URL updates as well as local storage updates
// and we want to make sure we are always using the latest preferences
const [reSync, setReSync] = useState(false);
const { preferences, loading, error } = usePreferenceLoader({
dataSource,
sourcePage,
reSync,
setReSync,
});
const { updateColumns, updateFormatting } = usePreferenceUpdater({
dataSource,
sourcePage,
mode,
preferences,
setReSync,

View File

@@ -1,12 +1,17 @@
import { SavedviewtypesSourceDTO } from 'api/generated/services/sigNoz.schemas';
import { TelemetryFieldKey } from 'api/v5/v5';
import { LogViewMode } from 'container/OptionsMenu/types';
import { FontSize } from 'container/OptionsMenu/types';
import { LogViewMode, FontSize } from 'container/OptionsMenu/types';
export enum PreferenceMode {
SAVED_VIEW = 'savedView',
DIRECT = 'direct',
}
// Only the logs and traces explorers keep column and formatting preferences.
export type PreferenceSourcePage =
| SavedviewtypesSourceDTO.logs
| SavedviewtypesSourceDTO.traces;
export interface PreferenceSlice {
preferences: Preferences | null;
loading: boolean;
@@ -15,10 +20,10 @@ export interface PreferenceSlice {
updateFormatting: (newFormatting: FormattingOptions) => void;
}
export interface PreferenceContextValue {
logs: PreferenceSlice;
traces: PreferenceSlice;
}
export type PreferenceContextValue = Record<
PreferenceSourcePage,
PreferenceSlice
>;
export interface FormattingOptions {
maxLines?: number;

View File

@@ -1,4 +1,5 @@
import { Dispatch, SetStateAction } from 'react';
import { SavedviewtypesSourceDTO } from 'api/generated/services/sigNoz.schemas';
import { TelemetryFieldKey } from 'api/v5/v5';
import {
defaultOptionsQuery,
@@ -6,48 +7,41 @@ import {
} from 'container/OptionsMenu/constants';
import { OptionsQuery } from 'container/OptionsMenu/types';
import useUrlQueryData from 'hooks/useUrlQueryData';
import { DataSource } from 'types/common/queryBuilder';
import getLogsUpdaterConfig from '../configs/logsUpdaterConfig';
import getTracesUpdaterConfig from '../configs/tracesUpdaterConfig';
import { FormattingOptions, Preferences } from '../types';
const metricsUpdater = {
updateColumns: (): void => {}, // no-op for metrics
updateFormatting: (): void => {}, // no-op for metrics
};
import { FormattingOptions, Preferences, PreferenceSourcePage } from '../types';
const getUpdaterConfig = (
preferences: Preferences | null,
redirectWithOptionsData: (options: OptionsQuery) => void,
setSavedViewPreferences: Dispatch<SetStateAction<Preferences | null>>,
): Record<
DataSource,
PreferenceSourcePage,
{
updateColumns: (newColumns: TelemetryFieldKey[], mode: string) => void;
updateFormatting: (newFormatting: FormattingOptions, mode: string) => void;
}
> => ({
[DataSource.LOGS]: getLogsUpdaterConfig(
[SavedviewtypesSourceDTO.logs]: getLogsUpdaterConfig(
preferences,
redirectWithOptionsData,
setSavedViewPreferences,
),
[DataSource.TRACES]: getTracesUpdaterConfig(
[SavedviewtypesSourceDTO.traces]: getTracesUpdaterConfig(
redirectWithOptionsData,
setSavedViewPreferences,
),
[DataSource.METRICS]: metricsUpdater,
});
export function usePreferenceUpdater({
dataSource,
sourcePage,
mode,
preferences,
setReSync,
setSavedViewPreferences,
}: {
dataSource: DataSource;
sourcePage: PreferenceSourcePage;
mode: string;
preferences: Preferences | null;
setReSync: Dispatch<SetStateAction<boolean>>;
@@ -62,7 +56,7 @@ export function usePreferenceUpdater({
preferences,
redirectWithOptionsData,
setSavedViewPreferences,
)[dataSource];
)[sourcePage];
return {
updateColumns: (newColumns: TelemetryFieldKey[]): void => {

View File

@@ -0,0 +1,11 @@
import { SavedviewtypesSourceDTO } from 'api/generated/services/sigNoz.schemas';
import { PreferenceSourcePage } from './types';
export function toPreferenceSourcePage(
sourcePage: SavedviewtypesSourceDTO,
): PreferenceSourcePage {
return sourcePage === SavedviewtypesSourceDTO.traces
? SavedviewtypesSourceDTO.traces
: SavedviewtypesSourceDTO.logs;
}

View File

@@ -28,7 +28,7 @@ const colServiceName = `resource_string_service$$$$name` // $ gets escaped so $$
// JSON holds the attributes.
var storableSpanColumns = []string{
"DISTINCT ON (span_id) timestamp", "duration_nano", "span_id", "has_error", "kind",
colServiceName, "name", "attributes", "resources_string", "scope",
colServiceName, "name", "attributes", "resources_string",
"if(notEmpty(attributes), map(), attributes_string) AS attributes_string",
"if(notEmpty(attributes), map(), attributes_number) AS attributes_number",
"if(notEmpty(attributes), map(), attributes_bool) AS attributes_bool",
@@ -382,7 +382,6 @@ func (s *traceStore) GetFlamegraphSpans(ctx context.Context, traceID string, sta
"if(notEmpty(attributes), map(), any(attributes_number)) AS attributes_number",
"if(notEmpty(attributes), map(), any(attributes_bool)) AS attributes_bool",
"any(resources_string) AS resources_string",
"any(scope) AS scope",
)
sb.From(fmt.Sprintf("%s.%s", spantypes.TraceDB, spantypes.TraceTable))
conditions := []string{

View File

@@ -154,7 +154,7 @@ func TestGetSpanCountByField(t *testing.T) {
}
func TestGetTraceSpans(t *testing.T) {
expectedSQL := "SELECT DISTINCT ON (span_id) timestamp, duration_nano, span_id, has_error, kind, resource_string_service$$name, name, attributes, resources_string, scope, if(notEmpty(attributes), map(), attributes_string) AS attributes_string, if(notEmpty(attributes), map(), attributes_number) AS attributes_number, if(notEmpty(attributes), map(), attributes_bool) AS attributes_bool, events, status_message, status_code_string, kind_string, parent_span_id, flags, is_remote, trace_state, status_code, db_name, db_operation, http_method, http_url, http_host, external_http_method, external_http_url, response_status_code, links as references FROM signoz_traces.distributed_signoz_index_v3 WHERE trace_id = ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? ORDER BY timestamp ASC, name ASC"
expectedSQL := "SELECT DISTINCT ON (span_id) timestamp, duration_nano, span_id, has_error, kind, resource_string_service$$name, name, attributes, resources_string, if(notEmpty(attributes), map(), attributes_string) AS attributes_string, if(notEmpty(attributes), map(), attributes_number) AS attributes_number, if(notEmpty(attributes), map(), attributes_bool) AS attributes_bool, events, status_message, status_code_string, kind_string, parent_span_id, flags, is_remote, trace_state, status_code, db_name, db_operation, http_method, http_url, http_host, external_http_method, external_http_url, response_status_code, links as references FROM signoz_traces.distributed_signoz_index_v3 WHERE trace_id = ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? ORDER BY timestamp ASC, name ASC"
s := newTestStore(sqlmock.QueryMatcherEqual)
s.Mock().ExpectSelect(expectedSQL).
@@ -171,12 +171,12 @@ func TestGetFlamegraphSpans(t *testing.T) {
}{
{
name: "NoSpanIDs_GeneratesBaseSQL",
sql: "SELECT span_id, any(parent_span_id) AS parent_span_id, any(timestamp) AS timestamp, any(duration_nano) AS duration_nano, any(has_error) AS has_error, any(name) AS name, any(events) AS events, any(attributes) AS attributes, if(notEmpty(attributes), map(), any(attributes_string)) AS attributes_string, if(notEmpty(attributes), map(), any(attributes_number)) AS attributes_number, if(notEmpty(attributes), map(), any(attributes_bool)) AS attributes_bool, any(resources_string) AS resources_string, any(scope) AS scope FROM signoz_traces.distributed_signoz_index_v3 WHERE trace_id = ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? GROUP BY span_id ORDER BY timestamp ASC, name ASC",
sql: "SELECT span_id, any(parent_span_id) AS parent_span_id, any(timestamp) AS timestamp, any(duration_nano) AS duration_nano, any(has_error) AS has_error, any(name) AS name, any(events) AS events, any(attributes) AS attributes, if(notEmpty(attributes), map(), any(attributes_string)) AS attributes_string, if(notEmpty(attributes), map(), any(attributes_number)) AS attributes_number, if(notEmpty(attributes), map(), any(attributes_bool)) AS attributes_bool, any(resources_string) AS resources_string FROM signoz_traces.distributed_signoz_index_v3 WHERE trace_id = ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? GROUP BY span_id ORDER BY timestamp ASC, name ASC",
},
{
name: "WithSpanIDs_GeneratesInClauseSQL",
spanIDs: []string{"span-1", "span-2"},
sql: "SELECT span_id, any(parent_span_id) AS parent_span_id, any(timestamp) AS timestamp, any(duration_nano) AS duration_nano, any(has_error) AS has_error, any(name) AS name, any(events) AS events, any(attributes) AS attributes, if(notEmpty(attributes), map(), any(attributes_string)) AS attributes_string, if(notEmpty(attributes), map(), any(attributes_number)) AS attributes_number, if(notEmpty(attributes), map(), any(attributes_bool)) AS attributes_bool, any(resources_string) AS resources_string, any(scope) AS scope FROM signoz_traces.distributed_signoz_index_v3 WHERE trace_id = ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND span_id IN (?, ?) GROUP BY span_id ORDER BY timestamp ASC, name ASC",
sql: "SELECT span_id, any(parent_span_id) AS parent_span_id, any(timestamp) AS timestamp, any(duration_nano) AS duration_nano, any(has_error) AS has_error, any(name) AS name, any(events) AS events, any(attributes) AS attributes, if(notEmpty(attributes), map(), any(attributes_string)) AS attributes_string, if(notEmpty(attributes), map(), any(attributes_number)) AS attributes_number, if(notEmpty(attributes), map(), any(attributes_bool)) AS attributes_bool, any(resources_string) AS resources_string FROM signoz_traces.distributed_signoz_index_v3 WHERE trace_id = ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND span_id IN (?, ?) GROUP BY span_id ORDER BY timestamp ASC, name ASC",
},
}

View File

@@ -95,9 +95,6 @@ func jsonColumn(paths map[string]any) telemetrystoretypes.JSONValue {
func TestUnwrapVariant(t *testing.T) {
jsonCol := jsonColumn(map[string]any{"level": "error", "attrs.code": int64(500)})
nullCol := jsonColumn(map[string]any{"level": "error", "absent": chcol.Variant{}})
scopeAttrs := chcol.NewJSON()
scopeAttrs.SetValueAtPath("lib.lang", "go")
scopeCol := jsonColumn(map[string]any{"name": "io.opentelemetry.http", "version": "1.2.0", "attributes": scopeAttrs})
testCases := []struct {
name string
@@ -111,7 +108,6 @@ func TestUnwrapVariant(t *testing.T) {
{name: "JSONColumnNested", column: "body_v2", input: jsonCol, want: map[string]any{"level": "error", "attrs": map[string]any{"code": float64(500)}}},
{name: "AttributesColumnFlat", column: "attributes", input: jsonCol, want: map[string]any{"level": "error", "attrs.code": float64(500)}},
{name: "AttributesColumnDropsNullPaths", column: "attributes", input: nullCol, want: map[string]any{"level": "error"}},
{name: "DeclaredObjectPathNestsFlat", column: "scope", input: scopeCol, want: map[string]any{"name": "io.opentelemetry.http", "version": "1.2.0", "attributes": map[string]any{"lib.lang": "go"}}},
}
for _, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) {

View File

@@ -45,7 +45,7 @@ SELECT timestamp AS __SELECT_KEY_0_timestamp, trace_id AS __SELECT_KEY_1_trace_i
http_url AS __SELECT_KEY_17_http_url, external_http_method AS __SELECT_KEY_18_external_http_method,
http_method AS __SELECT_KEY_19_http_method, http_host AS __SELECT_KEY_20_http_host, db_name AS __SELECT_KEY_21_db_name,
db_operation AS __SELECT_KEY_22_db_operation, has_error AS __SELECT_KEY_23_has_error, is_remote AS __SELECT_KEY_24_is_remote,
attributes_string, attributes_number, attributes_bool, resources_string, attributes, scope
attributes_string, attributes_number, attributes_bool, resources_string, attributes
FROM signoz_traces.distributed_signoz_index_v3
WHERE trace_id GLOBAL IN (SELECT trace_id FROM __trace_scope)
AND (((mapContains(attributes_string, 'gen_ai.request.model')

View File

@@ -676,7 +676,7 @@ SELECT timestamp AS __SELECT_KEY_0_timestamp, trace_id AS __SELECT_KEY_1_trace_i
http_url AS __SELECT_KEY_17_http_url, external_http_method AS __SELECT_KEY_18_external_http_method,
http_method AS __SELECT_KEY_19_http_method, http_host AS __SELECT_KEY_20_http_host, db_name AS __SELECT_KEY_21_db_name,
db_operation AS __SELECT_KEY_22_db_operation, has_error AS __SELECT_KEY_23_has_error, is_remote AS __SELECT_KEY_24_is_remote,
attributes_string, attributes_number, attributes_bool, resources_string, attributes, scope
attributes_string, attributes_number, attributes_bool, resources_string, attributes
FROM signoz_traces.distributed_signoz_index_v3
WHERE (((mapContains(attributes_string, 'gen_ai.request.model')
OR mapContains(attributes_string, 'gen_ai.tool.name')

View File

@@ -581,7 +581,7 @@ func TestStatementBuilderListQuery(t *testing.T) {
Limit: 10,
},
expected: qbtypes.Statement{
Query: "WITH __resource_filter AS (SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint) SELECT timestamp AS `__SELECT_KEY_0_timestamp`, trace_id AS `__SELECT_KEY_1_trace_id`, span_id AS `__SELECT_KEY_2_span_id`, trace_state AS `__SELECT_KEY_3_trace_state`, parent_span_id AS `__SELECT_KEY_4_parent_span_id`, flags AS `__SELECT_KEY_5_flags`, name AS `__SELECT_KEY_6_name`, kind AS `__SELECT_KEY_7_kind`, kind_string AS `__SELECT_KEY_8_kind_string`, duration_nano AS `__SELECT_KEY_9_duration_nano`, status_code AS `__SELECT_KEY_10_status_code`, status_message AS `__SELECT_KEY_11_status_message`, status_code_string AS `__SELECT_KEY_12_status_code_string`, events AS `__SELECT_KEY_13_events`, links AS `__SELECT_KEY_14_links`, response_status_code AS `__SELECT_KEY_15_response_status_code`, external_http_url AS `__SELECT_KEY_16_external_http_url`, http_url AS `__SELECT_KEY_17_http_url`, external_http_method AS `__SELECT_KEY_18_external_http_method`, http_method AS `__SELECT_KEY_19_http_method`, http_host AS `__SELECT_KEY_20_http_host`, db_name AS `__SELECT_KEY_21_db_name`, db_operation AS `__SELECT_KEY_22_db_operation`, has_error AS `__SELECT_KEY_23_has_error`, is_remote AS `__SELECT_KEY_24_is_remote`, attributes_string, attributes_number, attributes_bool, resources_string, attributes, scope FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? ORDER BY multiIf(mapContains(attributes_string, 'user.id'), attributes_string['user.id'], NULL) desc LIMIT ?",
Query: "WITH __resource_filter AS (SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint) SELECT timestamp AS `__SELECT_KEY_0_timestamp`, trace_id AS `__SELECT_KEY_1_trace_id`, span_id AS `__SELECT_KEY_2_span_id`, trace_state AS `__SELECT_KEY_3_trace_state`, parent_span_id AS `__SELECT_KEY_4_parent_span_id`, flags AS `__SELECT_KEY_5_flags`, name AS `__SELECT_KEY_6_name`, kind AS `__SELECT_KEY_7_kind`, kind_string AS `__SELECT_KEY_8_kind_string`, duration_nano AS `__SELECT_KEY_9_duration_nano`, status_code AS `__SELECT_KEY_10_status_code`, status_message AS `__SELECT_KEY_11_status_message`, status_code_string AS `__SELECT_KEY_12_status_code_string`, events AS `__SELECT_KEY_13_events`, links AS `__SELECT_KEY_14_links`, response_status_code AS `__SELECT_KEY_15_response_status_code`, external_http_url AS `__SELECT_KEY_16_external_http_url`, http_url AS `__SELECT_KEY_17_http_url`, external_http_method AS `__SELECT_KEY_18_external_http_method`, http_method AS `__SELECT_KEY_19_http_method`, http_host AS `__SELECT_KEY_20_http_host`, db_name AS `__SELECT_KEY_21_db_name`, db_operation AS `__SELECT_KEY_22_db_operation`, has_error AS `__SELECT_KEY_23_has_error`, is_remote AS `__SELECT_KEY_24_is_remote`, attributes_string, attributes_number, attributes_bool, resources_string, attributes FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter) AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? ORDER BY multiIf(mapContains(attributes_string, 'user.id'), attributes_string['user.id'], NULL) desc LIMIT ?",
Args: []any{"redis-manual", "%service.name%", "%service.name\":\"redis-manual%", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), 10},
},
expectedErr: nil,
@@ -837,7 +837,7 @@ func TestStatementBuilderListQueryWithCorruptData(t *testing.T) {
Limit: 10,
},
expected: qbtypes.Statement{
Query: "SELECT timestamp AS `__SELECT_KEY_0_timestamp`, trace_id AS `__SELECT_KEY_1_trace_id`, span_id AS `__SELECT_KEY_2_span_id`, trace_state AS `__SELECT_KEY_3_trace_state`, parent_span_id AS `__SELECT_KEY_4_parent_span_id`, flags AS `__SELECT_KEY_5_flags`, name AS `__SELECT_KEY_6_name`, kind AS `__SELECT_KEY_7_kind`, kind_string AS `__SELECT_KEY_8_kind_string`, duration_nano AS `__SELECT_KEY_9_duration_nano`, status_code AS `__SELECT_KEY_10_status_code`, status_message AS `__SELECT_KEY_11_status_message`, status_code_string AS `__SELECT_KEY_12_status_code_string`, events AS `__SELECT_KEY_13_events`, links AS `__SELECT_KEY_14_links`, response_status_code AS `__SELECT_KEY_15_response_status_code`, external_http_url AS `__SELECT_KEY_16_external_http_url`, http_url AS `__SELECT_KEY_17_http_url`, external_http_method AS `__SELECT_KEY_18_external_http_method`, http_method AS `__SELECT_KEY_19_http_method`, http_host AS `__SELECT_KEY_20_http_host`, db_name AS `__SELECT_KEY_21_db_name`, db_operation AS `__SELECT_KEY_22_db_operation`, has_error AS `__SELECT_KEY_23_has_error`, is_remote AS `__SELECT_KEY_24_is_remote`, attributes_string, attributes_number, attributes_bool, resources_string, attributes, scope FROM signoz_traces.distributed_signoz_index_v3 WHERE timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? LIMIT ?",
Query: "SELECT timestamp AS `__SELECT_KEY_0_timestamp`, trace_id AS `__SELECT_KEY_1_trace_id`, span_id AS `__SELECT_KEY_2_span_id`, trace_state AS `__SELECT_KEY_3_trace_state`, parent_span_id AS `__SELECT_KEY_4_parent_span_id`, flags AS `__SELECT_KEY_5_flags`, name AS `__SELECT_KEY_6_name`, kind AS `__SELECT_KEY_7_kind`, kind_string AS `__SELECT_KEY_8_kind_string`, duration_nano AS `__SELECT_KEY_9_duration_nano`, status_code AS `__SELECT_KEY_10_status_code`, status_message AS `__SELECT_KEY_11_status_message`, status_code_string AS `__SELECT_KEY_12_status_code_string`, events AS `__SELECT_KEY_13_events`, links AS `__SELECT_KEY_14_links`, response_status_code AS `__SELECT_KEY_15_response_status_code`, external_http_url AS `__SELECT_KEY_16_external_http_url`, http_url AS `__SELECT_KEY_17_http_url`, external_http_method AS `__SELECT_KEY_18_external_http_method`, http_method AS `__SELECT_KEY_19_http_method`, http_host AS `__SELECT_KEY_20_http_host`, db_name AS `__SELECT_KEY_21_db_name`, db_operation AS `__SELECT_KEY_22_db_operation`, has_error AS `__SELECT_KEY_23_has_error`, is_remote AS `__SELECT_KEY_24_is_remote`, attributes_string, attributes_number, attributes_bool, resources_string, attributes FROM signoz_traces.distributed_signoz_index_v3 WHERE timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? LIMIT ?",
Args: []any{"1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), 10},
},
expectedErr: nil,
@@ -879,7 +879,7 @@ func TestStatementBuilderListQueryWithCorruptData(t *testing.T) {
}},
},
expected: qbtypes.Statement{
Query: "SELECT timestamp AS `__SELECT_KEY_0_timestamp`, trace_id AS `__SELECT_KEY_1_trace_id`, span_id AS `__SELECT_KEY_2_span_id`, trace_state AS `__SELECT_KEY_3_trace_state`, parent_span_id AS `__SELECT_KEY_4_parent_span_id`, flags AS `__SELECT_KEY_5_flags`, name AS `__SELECT_KEY_6_name`, kind AS `__SELECT_KEY_7_kind`, kind_string AS `__SELECT_KEY_8_kind_string`, duration_nano AS `__SELECT_KEY_9_duration_nano`, status_code AS `__SELECT_KEY_10_status_code`, status_message AS `__SELECT_KEY_11_status_message`, status_code_string AS `__SELECT_KEY_12_status_code_string`, events AS `__SELECT_KEY_13_events`, links AS `__SELECT_KEY_14_links`, response_status_code AS `__SELECT_KEY_15_response_status_code`, external_http_url AS `__SELECT_KEY_16_external_http_url`, http_url AS `__SELECT_KEY_17_http_url`, external_http_method AS `__SELECT_KEY_18_external_http_method`, http_method AS `__SELECT_KEY_19_http_method`, http_host AS `__SELECT_KEY_20_http_host`, db_name AS `__SELECT_KEY_21_db_name`, db_operation AS `__SELECT_KEY_22_db_operation`, has_error AS `__SELECT_KEY_23_has_error`, is_remote AS `__SELECT_KEY_24_is_remote`, attributes_string, attributes_number, attributes_bool, resources_string, attributes, scope FROM signoz_traces.distributed_signoz_index_v3 WHERE timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? ORDER BY timestamp asc LIMIT ?",
Query: "SELECT timestamp AS `__SELECT_KEY_0_timestamp`, trace_id AS `__SELECT_KEY_1_trace_id`, span_id AS `__SELECT_KEY_2_span_id`, trace_state AS `__SELECT_KEY_3_trace_state`, parent_span_id AS `__SELECT_KEY_4_parent_span_id`, flags AS `__SELECT_KEY_5_flags`, name AS `__SELECT_KEY_6_name`, kind AS `__SELECT_KEY_7_kind`, kind_string AS `__SELECT_KEY_8_kind_string`, duration_nano AS `__SELECT_KEY_9_duration_nano`, status_code AS `__SELECT_KEY_10_status_code`, status_message AS `__SELECT_KEY_11_status_message`, status_code_string AS `__SELECT_KEY_12_status_code_string`, events AS `__SELECT_KEY_13_events`, links AS `__SELECT_KEY_14_links`, response_status_code AS `__SELECT_KEY_15_response_status_code`, external_http_url AS `__SELECT_KEY_16_external_http_url`, http_url AS `__SELECT_KEY_17_http_url`, external_http_method AS `__SELECT_KEY_18_external_http_method`, http_method AS `__SELECT_KEY_19_http_method`, http_host AS `__SELECT_KEY_20_http_host`, db_name AS `__SELECT_KEY_21_db_name`, db_operation AS `__SELECT_KEY_22_db_operation`, has_error AS `__SELECT_KEY_23_has_error`, is_remote AS `__SELECT_KEY_24_is_remote`, attributes_string, attributes_number, attributes_bool, resources_string, attributes FROM signoz_traces.distributed_signoz_index_v3 WHERE timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? ORDER BY timestamp asc LIMIT ?",
Args: []any{"1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), 10},
},
expectedErr: nil,
@@ -906,7 +906,7 @@ func TestStatementBuilderListQueryWithCorruptData(t *testing.T) {
Limit: 10,
},
expected: qbtypes.Statement{
Query: "SELECT timestamp AS `__SELECT_KEY_0_timestamp`, trace_id AS `__SELECT_KEY_1_trace_id`, span_id AS `__SELECT_KEY_2_span_id`, trace_state AS `__SELECT_KEY_3_trace_state`, parent_span_id AS `__SELECT_KEY_4_parent_span_id`, flags AS `__SELECT_KEY_5_flags`, name AS `__SELECT_KEY_6_name`, kind AS `__SELECT_KEY_7_kind`, kind_string AS `__SELECT_KEY_8_kind_string`, duration_nano AS `__SELECT_KEY_9_duration_nano`, status_code AS `__SELECT_KEY_10_status_code`, status_message AS `__SELECT_KEY_11_status_message`, status_code_string AS `__SELECT_KEY_12_status_code_string`, events AS `__SELECT_KEY_13_events`, links AS `__SELECT_KEY_14_links`, response_status_code AS `__SELECT_KEY_15_response_status_code`, external_http_url AS `__SELECT_KEY_16_external_http_url`, http_url AS `__SELECT_KEY_17_http_url`, external_http_method AS `__SELECT_KEY_18_external_http_method`, http_method AS `__SELECT_KEY_19_http_method`, http_host AS `__SELECT_KEY_20_http_host`, db_name AS `__SELECT_KEY_21_db_name`, db_operation AS `__SELECT_KEY_22_db_operation`, has_error AS `__SELECT_KEY_23_has_error`, is_remote AS `__SELECT_KEY_24_is_remote`, attributes_string, attributes_number, attributes_bool, resources_string, attributes, scope FROM signoz_traces.distributed_signoz_index_v3 WHERE (scope.version::String = ? AND scope.version::String <> '') AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? LIMIT ?",
Query: "SELECT timestamp AS `__SELECT_KEY_0_timestamp`, trace_id AS `__SELECT_KEY_1_trace_id`, span_id AS `__SELECT_KEY_2_span_id`, trace_state AS `__SELECT_KEY_3_trace_state`, parent_span_id AS `__SELECT_KEY_4_parent_span_id`, flags AS `__SELECT_KEY_5_flags`, name AS `__SELECT_KEY_6_name`, kind AS `__SELECT_KEY_7_kind`, kind_string AS `__SELECT_KEY_8_kind_string`, duration_nano AS `__SELECT_KEY_9_duration_nano`, status_code AS `__SELECT_KEY_10_status_code`, status_message AS `__SELECT_KEY_11_status_message`, status_code_string AS `__SELECT_KEY_12_status_code_string`, events AS `__SELECT_KEY_13_events`, links AS `__SELECT_KEY_14_links`, response_status_code AS `__SELECT_KEY_15_response_status_code`, external_http_url AS `__SELECT_KEY_16_external_http_url`, http_url AS `__SELECT_KEY_17_http_url`, external_http_method AS `__SELECT_KEY_18_external_http_method`, http_method AS `__SELECT_KEY_19_http_method`, http_host AS `__SELECT_KEY_20_http_host`, db_name AS `__SELECT_KEY_21_db_name`, db_operation AS `__SELECT_KEY_22_db_operation`, has_error AS `__SELECT_KEY_23_has_error`, is_remote AS `__SELECT_KEY_24_is_remote`, attributes_string, attributes_number, attributes_bool, resources_string, attributes FROM signoz_traces.distributed_signoz_index_v3 WHERE (scope.version::String = ? AND scope.version::String <> '') AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? LIMIT ?",
Args: []any{"1.0.0", "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), 10},
},
},

View File

@@ -124,7 +124,7 @@ func TestTraceOperatorStatementBuilder(t *testing.T) {
},
},
expected: qbtypes.Statement{
Query: "WITH toDateTime64(1747947419000000000, 9) AS t_from, toDateTime64(1747983448000000000, 9) AS t_to, 1747945619 AS bucket_from, 1747983448 AS bucket_to, all_spans AS (SELECT *, resource_string_service$$name AS `service.name` FROM signoz_traces.distributed_signoz_index_v3 WHERE timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), __resource_filter_A AS (SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint), A AS (SELECT * FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter_A) AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), __resource_filter_B AS (SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint), B AS (SELECT * FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter_B) AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), A_INDIR_DESC_B AS (WITH RECURSIVE up AS (SELECT d.trace_id, d.span_id, d.parent_span_id, 0 AS depth FROM B AS d UNION ALL SELECT p.trace_id, p.span_id, p.parent_span_id, up.depth + 1 FROM all_spans AS p JOIN up ON p.trace_id = up.trace_id AND p.span_id = up.parent_span_id WHERE up.depth < 100) SELECT DISTINCT a.* FROM A AS a GLOBAL INNER JOIN (SELECT DISTINCT trace_id, span_id FROM up WHERE depth > 0 ) AS ancestors ON ancestors.trace_id = a.trace_id AND ancestors.span_id = a.span_id) SELECT timestamp, trace_id, span_id, name, duration_nano, parent_span_id, trace_state AS `__SELECT_KEY_3_trace_state`, flags AS `__SELECT_KEY_5_flags`, kind AS `__SELECT_KEY_7_kind`, kind_string AS `__SELECT_KEY_8_kind_string`, status_code AS `__SELECT_KEY_10_status_code`, status_message AS `__SELECT_KEY_11_status_message`, status_code_string AS `__SELECT_KEY_12_status_code_string`, events AS `__SELECT_KEY_13_events`, links AS `__SELECT_KEY_14_links`, response_status_code AS `__SELECT_KEY_15_response_status_code`, external_http_url AS `__SELECT_KEY_16_external_http_url`, http_url AS `__SELECT_KEY_17_http_url`, external_http_method AS `__SELECT_KEY_18_external_http_method`, http_method AS `__SELECT_KEY_19_http_method`, http_host AS `__SELECT_KEY_20_http_host`, db_name AS `__SELECT_KEY_21_db_name`, db_operation AS `__SELECT_KEY_22_db_operation`, has_error AS `__SELECT_KEY_23_has_error`, is_remote AS `__SELECT_KEY_24_is_remote`, attributes_string, attributes_number, attributes_bool, resources_string, attributes, scope FROM A_INDIR_DESC_B ORDER BY timestamp DESC LIMIT ? SETTINGS distributed_product_mode='allow', max_memory_usage=10000000000",
Query: "WITH toDateTime64(1747947419000000000, 9) AS t_from, toDateTime64(1747983448000000000, 9) AS t_to, 1747945619 AS bucket_from, 1747983448 AS bucket_to, all_spans AS (SELECT *, resource_string_service$$name AS `service.name` FROM signoz_traces.distributed_signoz_index_v3 WHERE timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), __resource_filter_A AS (SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint), A AS (SELECT * FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter_A) AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), __resource_filter_B AS (SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint), B AS (SELECT * FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter_B) AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), A_INDIR_DESC_B AS (WITH RECURSIVE up AS (SELECT d.trace_id, d.span_id, d.parent_span_id, 0 AS depth FROM B AS d UNION ALL SELECT p.trace_id, p.span_id, p.parent_span_id, up.depth + 1 FROM all_spans AS p JOIN up ON p.trace_id = up.trace_id AND p.span_id = up.parent_span_id WHERE up.depth < 100) SELECT DISTINCT a.* FROM A AS a GLOBAL INNER JOIN (SELECT DISTINCT trace_id, span_id FROM up WHERE depth > 0 ) AS ancestors ON ancestors.trace_id = a.trace_id AND ancestors.span_id = a.span_id) SELECT timestamp, trace_id, span_id, name, duration_nano, parent_span_id, trace_state AS `__SELECT_KEY_3_trace_state`, flags AS `__SELECT_KEY_5_flags`, kind AS `__SELECT_KEY_7_kind`, kind_string AS `__SELECT_KEY_8_kind_string`, status_code AS `__SELECT_KEY_10_status_code`, status_message AS `__SELECT_KEY_11_status_message`, status_code_string AS `__SELECT_KEY_12_status_code_string`, events AS `__SELECT_KEY_13_events`, links AS `__SELECT_KEY_14_links`, response_status_code AS `__SELECT_KEY_15_response_status_code`, external_http_url AS `__SELECT_KEY_16_external_http_url`, http_url AS `__SELECT_KEY_17_http_url`, external_http_method AS `__SELECT_KEY_18_external_http_method`, http_method AS `__SELECT_KEY_19_http_method`, http_host AS `__SELECT_KEY_20_http_host`, db_name AS `__SELECT_KEY_21_db_name`, db_operation AS `__SELECT_KEY_22_db_operation`, has_error AS `__SELECT_KEY_23_has_error`, is_remote AS `__SELECT_KEY_24_is_remote`, attributes_string, attributes_number, attributes_bool, resources_string, attributes FROM A_INDIR_DESC_B ORDER BY timestamp DESC LIMIT ? SETTINGS distributed_product_mode='allow', max_memory_usage=10000000000",
Args: []any{"1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), "gateway", "%service.name%", "%service.name\":\"gateway%", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), "database", "%service.name%", "%service.name\":\"database%", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), 5},
},
expectedErr: nil,
@@ -161,7 +161,7 @@ func TestTraceOperatorStatementBuilder(t *testing.T) {
},
},
expected: qbtypes.Statement{
Query: "WITH toDateTime64(1747947419000000000, 9) AS t_from, toDateTime64(1747983448000000000, 9) AS t_to, 1747945619 AS bucket_from, 1747983448 AS bucket_to, all_spans AS (SELECT *, resource_string_service$$name AS `service.name` FROM signoz_traces.distributed_signoz_index_v3 WHERE timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), __resource_filter_A AS (SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint), A AS (SELECT * FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter_A) AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), __resource_filter_B AS (SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint), B AS (SELECT * FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter_B) AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), A_AND_B AS (SELECT l.* FROM A AS l INNER JOIN B AS r ON l.trace_id = r.trace_id) SELECT timestamp, trace_id, span_id, name, duration_nano, parent_span_id, trace_state AS `__SELECT_KEY_3_trace_state`, flags AS `__SELECT_KEY_5_flags`, kind AS `__SELECT_KEY_7_kind`, kind_string AS `__SELECT_KEY_8_kind_string`, status_code AS `__SELECT_KEY_10_status_code`, status_message AS `__SELECT_KEY_11_status_message`, status_code_string AS `__SELECT_KEY_12_status_code_string`, events AS `__SELECT_KEY_13_events`, links AS `__SELECT_KEY_14_links`, response_status_code AS `__SELECT_KEY_15_response_status_code`, external_http_url AS `__SELECT_KEY_16_external_http_url`, http_url AS `__SELECT_KEY_17_http_url`, external_http_method AS `__SELECT_KEY_18_external_http_method`, http_method AS `__SELECT_KEY_19_http_method`, http_host AS `__SELECT_KEY_20_http_host`, db_name AS `__SELECT_KEY_21_db_name`, db_operation AS `__SELECT_KEY_22_db_operation`, has_error AS `__SELECT_KEY_23_has_error`, is_remote AS `__SELECT_KEY_24_is_remote`, attributes_string, attributes_number, attributes_bool, resources_string, attributes, scope FROM A_AND_B ORDER BY timestamp DESC LIMIT ? SETTINGS distributed_product_mode='allow', max_memory_usage=10000000000",
Query: "WITH toDateTime64(1747947419000000000, 9) AS t_from, toDateTime64(1747983448000000000, 9) AS t_to, 1747945619 AS bucket_from, 1747983448 AS bucket_to, all_spans AS (SELECT *, resource_string_service$$name AS `service.name` FROM signoz_traces.distributed_signoz_index_v3 WHERE timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), __resource_filter_A AS (SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint), A AS (SELECT * FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter_A) AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), __resource_filter_B AS (SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint), B AS (SELECT * FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter_B) AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), A_AND_B AS (SELECT l.* FROM A AS l INNER JOIN B AS r ON l.trace_id = r.trace_id) SELECT timestamp, trace_id, span_id, name, duration_nano, parent_span_id, trace_state AS `__SELECT_KEY_3_trace_state`, flags AS `__SELECT_KEY_5_flags`, kind AS `__SELECT_KEY_7_kind`, kind_string AS `__SELECT_KEY_8_kind_string`, status_code AS `__SELECT_KEY_10_status_code`, status_message AS `__SELECT_KEY_11_status_message`, status_code_string AS `__SELECT_KEY_12_status_code_string`, events AS `__SELECT_KEY_13_events`, links AS `__SELECT_KEY_14_links`, response_status_code AS `__SELECT_KEY_15_response_status_code`, external_http_url AS `__SELECT_KEY_16_external_http_url`, http_url AS `__SELECT_KEY_17_http_url`, external_http_method AS `__SELECT_KEY_18_external_http_method`, http_method AS `__SELECT_KEY_19_http_method`, http_host AS `__SELECT_KEY_20_http_host`, db_name AS `__SELECT_KEY_21_db_name`, db_operation AS `__SELECT_KEY_22_db_operation`, has_error AS `__SELECT_KEY_23_has_error`, is_remote AS `__SELECT_KEY_24_is_remote`, attributes_string, attributes_number, attributes_bool, resources_string, attributes FROM A_AND_B ORDER BY timestamp DESC LIMIT ? SETTINGS distributed_product_mode='allow', max_memory_usage=10000000000",
Args: []any{"1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), "frontend", "%service.name%", "%service.name\":\"frontend%", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), "backend", "%service.name%", "%service.name\":\"backend%", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), 15},
},
expectedErr: nil,
@@ -198,7 +198,7 @@ func TestTraceOperatorStatementBuilder(t *testing.T) {
},
},
expected: qbtypes.Statement{
Query: "WITH toDateTime64(1747947419000000000, 9) AS t_from, toDateTime64(1747983448000000000, 9) AS t_to, 1747945619 AS bucket_from, 1747983448 AS bucket_to, all_spans AS (SELECT *, resource_string_service$$name AS `service.name` FROM signoz_traces.distributed_signoz_index_v3 WHERE timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), __resource_filter_A AS (SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint), A AS (SELECT * FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter_A) AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), __resource_filter_B AS (SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint), B AS (SELECT * FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter_B) AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), A_OR_B AS (SELECT * FROM A UNION DISTINCT SELECT * FROM B) SELECT timestamp, trace_id, span_id, name, duration_nano, parent_span_id, trace_state AS `__SELECT_KEY_3_trace_state`, flags AS `__SELECT_KEY_5_flags`, kind AS `__SELECT_KEY_7_kind`, kind_string AS `__SELECT_KEY_8_kind_string`, status_code AS `__SELECT_KEY_10_status_code`, status_message AS `__SELECT_KEY_11_status_message`, status_code_string AS `__SELECT_KEY_12_status_code_string`, events AS `__SELECT_KEY_13_events`, links AS `__SELECT_KEY_14_links`, response_status_code AS `__SELECT_KEY_15_response_status_code`, external_http_url AS `__SELECT_KEY_16_external_http_url`, http_url AS `__SELECT_KEY_17_http_url`, external_http_method AS `__SELECT_KEY_18_external_http_method`, http_method AS `__SELECT_KEY_19_http_method`, http_host AS `__SELECT_KEY_20_http_host`, db_name AS `__SELECT_KEY_21_db_name`, db_operation AS `__SELECT_KEY_22_db_operation`, has_error AS `__SELECT_KEY_23_has_error`, is_remote AS `__SELECT_KEY_24_is_remote`, attributes_string, attributes_number, attributes_bool, resources_string, attributes, scope FROM A_OR_B ORDER BY timestamp DESC LIMIT ? SETTINGS distributed_product_mode='allow', max_memory_usage=10000000000",
Query: "WITH toDateTime64(1747947419000000000, 9) AS t_from, toDateTime64(1747983448000000000, 9) AS t_to, 1747945619 AS bucket_from, 1747983448 AS bucket_to, all_spans AS (SELECT *, resource_string_service$$name AS `service.name` FROM signoz_traces.distributed_signoz_index_v3 WHERE timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), __resource_filter_A AS (SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint), A AS (SELECT * FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter_A) AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), __resource_filter_B AS (SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint), B AS (SELECT * FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter_B) AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), A_OR_B AS (SELECT * FROM A UNION DISTINCT SELECT * FROM B) SELECT timestamp, trace_id, span_id, name, duration_nano, parent_span_id, trace_state AS `__SELECT_KEY_3_trace_state`, flags AS `__SELECT_KEY_5_flags`, kind AS `__SELECT_KEY_7_kind`, kind_string AS `__SELECT_KEY_8_kind_string`, status_code AS `__SELECT_KEY_10_status_code`, status_message AS `__SELECT_KEY_11_status_message`, status_code_string AS `__SELECT_KEY_12_status_code_string`, events AS `__SELECT_KEY_13_events`, links AS `__SELECT_KEY_14_links`, response_status_code AS `__SELECT_KEY_15_response_status_code`, external_http_url AS `__SELECT_KEY_16_external_http_url`, http_url AS `__SELECT_KEY_17_http_url`, external_http_method AS `__SELECT_KEY_18_external_http_method`, http_method AS `__SELECT_KEY_19_http_method`, http_host AS `__SELECT_KEY_20_http_host`, db_name AS `__SELECT_KEY_21_db_name`, db_operation AS `__SELECT_KEY_22_db_operation`, has_error AS `__SELECT_KEY_23_has_error`, is_remote AS `__SELECT_KEY_24_is_remote`, attributes_string, attributes_number, attributes_bool, resources_string, attributes FROM A_OR_B ORDER BY timestamp DESC LIMIT ? SETTINGS distributed_product_mode='allow', max_memory_usage=10000000000",
Args: []any{"1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), "frontend", "%service.name%", "%service.name\":\"frontend%", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), "backend", "%service.name%", "%service.name\":\"backend%", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), 20},
},
expectedErr: nil,
@@ -235,7 +235,7 @@ func TestTraceOperatorStatementBuilder(t *testing.T) {
},
},
expected: qbtypes.Statement{
Query: "WITH toDateTime64(1747947419000000000, 9) AS t_from, toDateTime64(1747983448000000000, 9) AS t_to, 1747945619 AS bucket_from, 1747983448 AS bucket_to, all_spans AS (SELECT *, resource_string_service$$name AS `service.name` FROM signoz_traces.distributed_signoz_index_v3 WHERE timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), __resource_filter_A AS (SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint), A AS (SELECT * FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter_A) AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), __resource_filter_B AS (SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint), B AS (SELECT * FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter_B) AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), A_not_B AS (SELECT l.* FROM A AS l WHERE l.trace_id GLOBAL NOT IN (SELECT DISTINCT trace_id FROM B)) SELECT timestamp, trace_id, span_id, name, duration_nano, parent_span_id, trace_state AS `__SELECT_KEY_3_trace_state`, flags AS `__SELECT_KEY_5_flags`, kind AS `__SELECT_KEY_7_kind`, kind_string AS `__SELECT_KEY_8_kind_string`, status_code AS `__SELECT_KEY_10_status_code`, status_message AS `__SELECT_KEY_11_status_message`, status_code_string AS `__SELECT_KEY_12_status_code_string`, events AS `__SELECT_KEY_13_events`, links AS `__SELECT_KEY_14_links`, response_status_code AS `__SELECT_KEY_15_response_status_code`, external_http_url AS `__SELECT_KEY_16_external_http_url`, http_url AS `__SELECT_KEY_17_http_url`, external_http_method AS `__SELECT_KEY_18_external_http_method`, http_method AS `__SELECT_KEY_19_http_method`, http_host AS `__SELECT_KEY_20_http_host`, db_name AS `__SELECT_KEY_21_db_name`, db_operation AS `__SELECT_KEY_22_db_operation`, has_error AS `__SELECT_KEY_23_has_error`, is_remote AS `__SELECT_KEY_24_is_remote`, attributes_string, attributes_number, attributes_bool, resources_string, attributes, scope FROM A_not_B ORDER BY timestamp DESC LIMIT ? SETTINGS distributed_product_mode='allow', max_memory_usage=10000000000",
Query: "WITH toDateTime64(1747947419000000000, 9) AS t_from, toDateTime64(1747983448000000000, 9) AS t_to, 1747945619 AS bucket_from, 1747983448 AS bucket_to, all_spans AS (SELECT *, resource_string_service$$name AS `service.name` FROM signoz_traces.distributed_signoz_index_v3 WHERE timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), __resource_filter_A AS (SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint), A AS (SELECT * FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter_A) AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), __resource_filter_B AS (SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint), B AS (SELECT * FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter_B) AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), A_not_B AS (SELECT l.* FROM A AS l WHERE l.trace_id GLOBAL NOT IN (SELECT DISTINCT trace_id FROM B)) SELECT timestamp, trace_id, span_id, name, duration_nano, parent_span_id, trace_state AS `__SELECT_KEY_3_trace_state`, flags AS `__SELECT_KEY_5_flags`, kind AS `__SELECT_KEY_7_kind`, kind_string AS `__SELECT_KEY_8_kind_string`, status_code AS `__SELECT_KEY_10_status_code`, status_message AS `__SELECT_KEY_11_status_message`, status_code_string AS `__SELECT_KEY_12_status_code_string`, events AS `__SELECT_KEY_13_events`, links AS `__SELECT_KEY_14_links`, response_status_code AS `__SELECT_KEY_15_response_status_code`, external_http_url AS `__SELECT_KEY_16_external_http_url`, http_url AS `__SELECT_KEY_17_http_url`, external_http_method AS `__SELECT_KEY_18_external_http_method`, http_method AS `__SELECT_KEY_19_http_method`, http_host AS `__SELECT_KEY_20_http_host`, db_name AS `__SELECT_KEY_21_db_name`, db_operation AS `__SELECT_KEY_22_db_operation`, has_error AS `__SELECT_KEY_23_has_error`, is_remote AS `__SELECT_KEY_24_is_remote`, attributes_string, attributes_number, attributes_bool, resources_string, attributes FROM A_not_B ORDER BY timestamp DESC LIMIT ? SETTINGS distributed_product_mode='allow', max_memory_usage=10000000000",
Args: []any{"1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), "frontend", "%service.name%", "%service.name\":\"frontend%", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), "backend", "%service.name%", "%service.name\":\"backend%", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), 10},
},
expectedErr: nil,
@@ -400,7 +400,7 @@ func TestTraceOperatorStatementBuilder(t *testing.T) {
},
},
expected: qbtypes.Statement{
Query: "WITH toDateTime64(1747947419000000000, 9) AS t_from, toDateTime64(1747983448000000000, 9) AS t_to, 1747945619 AS bucket_from, 1747983448 AS bucket_to, all_spans AS (SELECT *, resource_string_service$$name AS `service.name` FROM signoz_traces.distributed_signoz_index_v3 WHERE timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), __resource_filter_A AS (SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint), A AS (SELECT * FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter_A) AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), __resource_filter_B AS (SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint), B AS (SELECT * FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter_B) AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), A_DIR_DESC_B AS (SELECT p.* FROM A AS p INNER JOIN B AS c ON p.trace_id = c.trace_id AND p.span_id = c.parent_span_id), __resource_filter_C AS (SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint), C AS (SELECT * FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter_C) AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), __resource_filter_D AS (SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint), D AS (SELECT * FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter_D) AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), C_DIR_DESC_D AS (SELECT p.* FROM C AS p INNER JOIN D AS c ON p.trace_id = c.trace_id AND p.span_id = c.parent_span_id), A_DIR_DESC_B_AND_C_DIR_DESC_D AS (SELECT l.* FROM A_DIR_DESC_B AS l INNER JOIN C_DIR_DESC_D AS r ON l.trace_id = r.trace_id) SELECT timestamp, trace_id, span_id, name, duration_nano, parent_span_id, trace_state AS `__SELECT_KEY_3_trace_state`, flags AS `__SELECT_KEY_5_flags`, kind AS `__SELECT_KEY_7_kind`, kind_string AS `__SELECT_KEY_8_kind_string`, status_code AS `__SELECT_KEY_10_status_code`, status_message AS `__SELECT_KEY_11_status_message`, status_code_string AS `__SELECT_KEY_12_status_code_string`, events AS `__SELECT_KEY_13_events`, links AS `__SELECT_KEY_14_links`, response_status_code AS `__SELECT_KEY_15_response_status_code`, external_http_url AS `__SELECT_KEY_16_external_http_url`, http_url AS `__SELECT_KEY_17_http_url`, external_http_method AS `__SELECT_KEY_18_external_http_method`, http_method AS `__SELECT_KEY_19_http_method`, http_host AS `__SELECT_KEY_20_http_host`, db_name AS `__SELECT_KEY_21_db_name`, db_operation AS `__SELECT_KEY_22_db_operation`, has_error AS `__SELECT_KEY_23_has_error`, is_remote AS `__SELECT_KEY_24_is_remote`, attributes_string, attributes_number, attributes_bool, resources_string, attributes, scope FROM A_DIR_DESC_B_AND_C_DIR_DESC_D ORDER BY timestamp DESC LIMIT ? SETTINGS distributed_product_mode='allow', max_memory_usage=10000000000",
Query: "WITH toDateTime64(1747947419000000000, 9) AS t_from, toDateTime64(1747983448000000000, 9) AS t_to, 1747945619 AS bucket_from, 1747983448 AS bucket_to, all_spans AS (SELECT *, resource_string_service$$name AS `service.name` FROM signoz_traces.distributed_signoz_index_v3 WHERE timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), __resource_filter_A AS (SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint), A AS (SELECT * FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter_A) AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), __resource_filter_B AS (SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint), B AS (SELECT * FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter_B) AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), A_DIR_DESC_B AS (SELECT p.* FROM A AS p INNER JOIN B AS c ON p.trace_id = c.trace_id AND p.span_id = c.parent_span_id), __resource_filter_C AS (SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint), C AS (SELECT * FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter_C) AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), __resource_filter_D AS (SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint), D AS (SELECT * FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter_D) AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), C_DIR_DESC_D AS (SELECT p.* FROM C AS p INNER JOIN D AS c ON p.trace_id = c.trace_id AND p.span_id = c.parent_span_id), A_DIR_DESC_B_AND_C_DIR_DESC_D AS (SELECT l.* FROM A_DIR_DESC_B AS l INNER JOIN C_DIR_DESC_D AS r ON l.trace_id = r.trace_id) SELECT timestamp, trace_id, span_id, name, duration_nano, parent_span_id, trace_state AS `__SELECT_KEY_3_trace_state`, flags AS `__SELECT_KEY_5_flags`, kind AS `__SELECT_KEY_7_kind`, kind_string AS `__SELECT_KEY_8_kind_string`, status_code AS `__SELECT_KEY_10_status_code`, status_message AS `__SELECT_KEY_11_status_message`, status_code_string AS `__SELECT_KEY_12_status_code_string`, events AS `__SELECT_KEY_13_events`, links AS `__SELECT_KEY_14_links`, response_status_code AS `__SELECT_KEY_15_response_status_code`, external_http_url AS `__SELECT_KEY_16_external_http_url`, http_url AS `__SELECT_KEY_17_http_url`, external_http_method AS `__SELECT_KEY_18_external_http_method`, http_method AS `__SELECT_KEY_19_http_method`, http_host AS `__SELECT_KEY_20_http_host`, db_name AS `__SELECT_KEY_21_db_name`, db_operation AS `__SELECT_KEY_22_db_operation`, has_error AS `__SELECT_KEY_23_has_error`, is_remote AS `__SELECT_KEY_24_is_remote`, attributes_string, attributes_number, attributes_bool, resources_string, attributes FROM A_DIR_DESC_B_AND_C_DIR_DESC_D ORDER BY timestamp DESC LIMIT ? SETTINGS distributed_product_mode='allow', max_memory_usage=10000000000",
Args: []any{"1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), "frontend", "%service.name%", "%service.name\":\"frontend%", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), "backend", "%service.name%", "%service.name\":\"backend%", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), "auth", "%service.name%", "%service.name\":\"auth%", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), "database", "%service.name%", "%service.name\":\"database%", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), 5},
},
expectedErr: nil,
@@ -434,7 +434,7 @@ func TestTraceOperatorStatementBuilder(t *testing.T) {
},
},
expected: qbtypes.Statement{
Query: "WITH toDateTime64(1747947419000000000, 9) AS t_from, toDateTime64(1747983448000000000, 9) AS t_to, 1747945619 AS bucket_from, 1747983448 AS bucket_to, all_spans AS (SELECT *, resource_string_service$$name AS `service.name` FROM signoz_traces.distributed_signoz_index_v3 WHERE timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), __resource_filter_A AS (SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint), A AS (SELECT * FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter_A) AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), __resource_filter_B AS (SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint), B AS (SELECT * FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter_B) AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), A_INDIR_DESC_B AS (WITH RECURSIVE up AS (SELECT d.trace_id, d.span_id, d.parent_span_id, 0 AS depth FROM B AS d UNION ALL SELECT p.trace_id, p.span_id, p.parent_span_id, up.depth + 1 FROM all_spans AS p JOIN up ON p.trace_id = up.trace_id AND p.span_id = up.parent_span_id WHERE up.depth < 100) SELECT DISTINCT a.* FROM A AS a GLOBAL INNER JOIN (SELECT DISTINCT trace_id, span_id FROM up WHERE depth > 0 ) AS ancestors ON ancestors.trace_id = a.trace_id AND ancestors.span_id = a.span_id), __return_from_B AS (SELECT * FROM B WHERE trace_id IN (SELECT DISTINCT trace_id FROM A_INDIR_DESC_B)) SELECT timestamp, trace_id, span_id, name, duration_nano, parent_span_id, trace_state AS `__SELECT_KEY_3_trace_state`, flags AS `__SELECT_KEY_5_flags`, kind AS `__SELECT_KEY_7_kind`, kind_string AS `__SELECT_KEY_8_kind_string`, status_code AS `__SELECT_KEY_10_status_code`, status_message AS `__SELECT_KEY_11_status_message`, status_code_string AS `__SELECT_KEY_12_status_code_string`, events AS `__SELECT_KEY_13_events`, links AS `__SELECT_KEY_14_links`, response_status_code AS `__SELECT_KEY_15_response_status_code`, external_http_url AS `__SELECT_KEY_16_external_http_url`, http_url AS `__SELECT_KEY_17_http_url`, external_http_method AS `__SELECT_KEY_18_external_http_method`, http_method AS `__SELECT_KEY_19_http_method`, http_host AS `__SELECT_KEY_20_http_host`, db_name AS `__SELECT_KEY_21_db_name`, db_operation AS `__SELECT_KEY_22_db_operation`, has_error AS `__SELECT_KEY_23_has_error`, is_remote AS `__SELECT_KEY_24_is_remote`, attributes_string, attributes_number, attributes_bool, resources_string, attributes, scope FROM __return_from_B ORDER BY timestamp DESC LIMIT ? SETTINGS distributed_product_mode='allow', max_memory_usage=10000000000",
Query: "WITH toDateTime64(1747947419000000000, 9) AS t_from, toDateTime64(1747983448000000000, 9) AS t_to, 1747945619 AS bucket_from, 1747983448 AS bucket_to, all_spans AS (SELECT *, resource_string_service$$name AS `service.name` FROM signoz_traces.distributed_signoz_index_v3 WHERE timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), __resource_filter_A AS (SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint), A AS (SELECT * FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter_A) AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), __resource_filter_B AS (SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint), B AS (SELECT * FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter_B) AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), A_INDIR_DESC_B AS (WITH RECURSIVE up AS (SELECT d.trace_id, d.span_id, d.parent_span_id, 0 AS depth FROM B AS d UNION ALL SELECT p.trace_id, p.span_id, p.parent_span_id, up.depth + 1 FROM all_spans AS p JOIN up ON p.trace_id = up.trace_id AND p.span_id = up.parent_span_id WHERE up.depth < 100) SELECT DISTINCT a.* FROM A AS a GLOBAL INNER JOIN (SELECT DISTINCT trace_id, span_id FROM up WHERE depth > 0 ) AS ancestors ON ancestors.trace_id = a.trace_id AND ancestors.span_id = a.span_id), __return_from_B AS (SELECT * FROM B WHERE trace_id IN (SELECT DISTINCT trace_id FROM A_INDIR_DESC_B)) SELECT timestamp, trace_id, span_id, name, duration_nano, parent_span_id, trace_state AS `__SELECT_KEY_3_trace_state`, flags AS `__SELECT_KEY_5_flags`, kind AS `__SELECT_KEY_7_kind`, kind_string AS `__SELECT_KEY_8_kind_string`, status_code AS `__SELECT_KEY_10_status_code`, status_message AS `__SELECT_KEY_11_status_message`, status_code_string AS `__SELECT_KEY_12_status_code_string`, events AS `__SELECT_KEY_13_events`, links AS `__SELECT_KEY_14_links`, response_status_code AS `__SELECT_KEY_15_response_status_code`, external_http_url AS `__SELECT_KEY_16_external_http_url`, http_url AS `__SELECT_KEY_17_http_url`, external_http_method AS `__SELECT_KEY_18_external_http_method`, http_method AS `__SELECT_KEY_19_http_method`, http_host AS `__SELECT_KEY_20_http_host`, db_name AS `__SELECT_KEY_21_db_name`, db_operation AS `__SELECT_KEY_22_db_operation`, has_error AS `__SELECT_KEY_23_has_error`, is_remote AS `__SELECT_KEY_24_is_remote`, attributes_string, attributes_number, attributes_bool, resources_string, attributes FROM __return_from_B ORDER BY timestamp DESC LIMIT ? SETTINGS distributed_product_mode='allow', max_memory_usage=10000000000",
Args: []any{"1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), "gateway", "%service.name%", "%service.name\":\"gateway%", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), "database", "%service.name%", "%service.name\":\"database%", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), 10},
},
expectedErr: nil,
@@ -476,7 +476,7 @@ func TestTraceOperatorStatementBuilder(t *testing.T) {
},
},
expected: qbtypes.Statement{
Query: "WITH toDateTime64(1747947419000000000, 9) AS t_from, toDateTime64(1747983448000000000, 9) AS t_to, 1747945619 AS bucket_from, 1747983448 AS bucket_to, all_spans AS (SELECT *, resource_string_service$$name AS `service.name` FROM signoz_traces.distributed_signoz_index_v3 WHERE timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), __resource_filter_A AS (SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint), A AS (SELECT * FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter_A) AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), __resource_filter_B AS (SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint), B AS (SELECT * FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter_B) AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), A_INDIR_DESC_B AS (WITH RECURSIVE up AS (SELECT d.trace_id, d.span_id, d.parent_span_id, 0 AS depth FROM B AS d UNION ALL SELECT p.trace_id, p.span_id, p.parent_span_id, up.depth + 1 FROM all_spans AS p JOIN up ON p.trace_id = up.trace_id AND p.span_id = up.parent_span_id WHERE up.depth < 100) SELECT DISTINCT a.* FROM A AS a GLOBAL INNER JOIN (SELECT DISTINCT trace_id, span_id FROM up WHERE depth > 0 ) AS ancestors ON ancestors.trace_id = a.trace_id AND ancestors.span_id = a.span_id), __resource_filter_C AS (SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint), C AS (SELECT * FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter_C) AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), A_INDIR_DESC_B_AND_C AS (SELECT l.* FROM A_INDIR_DESC_B AS l INNER JOIN C AS r ON l.trace_id = r.trace_id), __return_from_C AS (SELECT * FROM C WHERE trace_id IN (SELECT DISTINCT trace_id FROM A_INDIR_DESC_B_AND_C)) SELECT timestamp, trace_id, span_id, name, duration_nano, parent_span_id, trace_state AS `__SELECT_KEY_3_trace_state`, flags AS `__SELECT_KEY_5_flags`, kind AS `__SELECT_KEY_7_kind`, kind_string AS `__SELECT_KEY_8_kind_string`, status_code AS `__SELECT_KEY_10_status_code`, status_message AS `__SELECT_KEY_11_status_message`, status_code_string AS `__SELECT_KEY_12_status_code_string`, events AS `__SELECT_KEY_13_events`, links AS `__SELECT_KEY_14_links`, response_status_code AS `__SELECT_KEY_15_response_status_code`, external_http_url AS `__SELECT_KEY_16_external_http_url`, http_url AS `__SELECT_KEY_17_http_url`, external_http_method AS `__SELECT_KEY_18_external_http_method`, http_method AS `__SELECT_KEY_19_http_method`, http_host AS `__SELECT_KEY_20_http_host`, db_name AS `__SELECT_KEY_21_db_name`, db_operation AS `__SELECT_KEY_22_db_operation`, has_error AS `__SELECT_KEY_23_has_error`, is_remote AS `__SELECT_KEY_24_is_remote`, attributes_string, attributes_number, attributes_bool, resources_string, attributes, scope FROM __return_from_C ORDER BY timestamp DESC LIMIT ? SETTINGS distributed_product_mode='allow', max_memory_usage=10000000000",
Query: "WITH toDateTime64(1747947419000000000, 9) AS t_from, toDateTime64(1747983448000000000, 9) AS t_to, 1747945619 AS bucket_from, 1747983448 AS bucket_to, all_spans AS (SELECT *, resource_string_service$$name AS `service.name` FROM signoz_traces.distributed_signoz_index_v3 WHERE timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), __resource_filter_A AS (SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint), A AS (SELECT * FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter_A) AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), __resource_filter_B AS (SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint), B AS (SELECT * FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter_B) AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), A_INDIR_DESC_B AS (WITH RECURSIVE up AS (SELECT d.trace_id, d.span_id, d.parent_span_id, 0 AS depth FROM B AS d UNION ALL SELECT p.trace_id, p.span_id, p.parent_span_id, up.depth + 1 FROM all_spans AS p JOIN up ON p.trace_id = up.trace_id AND p.span_id = up.parent_span_id WHERE up.depth < 100) SELECT DISTINCT a.* FROM A AS a GLOBAL INNER JOIN (SELECT DISTINCT trace_id, span_id FROM up WHERE depth > 0 ) AS ancestors ON ancestors.trace_id = a.trace_id AND ancestors.span_id = a.span_id), __resource_filter_C AS (SELECT fingerprint FROM signoz_traces.distributed_traces_v3_resource WHERE (simpleJSONExtractString(labels, 'service.name') = ? AND labels LIKE ? AND labels LIKE ?) AND seen_at_ts_bucket_start >= ? AND seen_at_ts_bucket_start <= ? GROUP BY fingerprint), C AS (SELECT * FROM signoz_traces.distributed_signoz_index_v3 WHERE resource_fingerprint GLOBAL IN (SELECT fingerprint FROM __resource_filter_C) AND timestamp >= ? AND timestamp < ? AND ts_bucket_start >= ? AND ts_bucket_start <= ?), A_INDIR_DESC_B_AND_C AS (SELECT l.* FROM A_INDIR_DESC_B AS l INNER JOIN C AS r ON l.trace_id = r.trace_id), __return_from_C AS (SELECT * FROM C WHERE trace_id IN (SELECT DISTINCT trace_id FROM A_INDIR_DESC_B_AND_C)) SELECT timestamp, trace_id, span_id, name, duration_nano, parent_span_id, trace_state AS `__SELECT_KEY_3_trace_state`, flags AS `__SELECT_KEY_5_flags`, kind AS `__SELECT_KEY_7_kind`, kind_string AS `__SELECT_KEY_8_kind_string`, status_code AS `__SELECT_KEY_10_status_code`, status_message AS `__SELECT_KEY_11_status_message`, status_code_string AS `__SELECT_KEY_12_status_code_string`, events AS `__SELECT_KEY_13_events`, links AS `__SELECT_KEY_14_links`, response_status_code AS `__SELECT_KEY_15_response_status_code`, external_http_url AS `__SELECT_KEY_16_external_http_url`, http_url AS `__SELECT_KEY_17_http_url`, external_http_method AS `__SELECT_KEY_18_external_http_method`, http_method AS `__SELECT_KEY_19_http_method`, http_host AS `__SELECT_KEY_20_http_host`, db_name AS `__SELECT_KEY_21_db_name`, db_operation AS `__SELECT_KEY_22_db_operation`, has_error AS `__SELECT_KEY_23_has_error`, is_remote AS `__SELECT_KEY_24_is_remote`, attributes_string, attributes_number, attributes_bool, resources_string, attributes FROM __return_from_C ORDER BY timestamp DESC LIMIT ? SETTINGS distributed_product_mode='allow', max_memory_usage=10000000000",
Args: []any{"1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), "gateway", "%service.name%", "%service.name\":\"gateway%", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), "database", "%service.name%", "%service.name\":\"database%", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), "auth", "%service.name%", "%service.name\":\"auth%", uint64(1747945619), uint64(1747983448), "1747947419000000000", "1747983448000000000", uint64(1747945619), uint64(1747983448), 10},
},
expectedErr: nil,

View File

@@ -46,7 +46,6 @@ const (
SpanAttributesColumn = "attributes"
SpanAttributesPromotedColumn = "attributes_promoted"
SpanResourcesStringColumn = "resources_string"
SpanScopeColumn = "scope"
)
var (
@@ -456,7 +455,6 @@ var (
SpanAttributesBoolColumn,
SpanResourcesStringColumn,
SpanAttributesColumn,
SpanScopeColumn,
}
DefaultFields = map[string]telemetrytypes.TelemetryFieldKey{

View File

@@ -17,7 +17,6 @@ type FlamegraphSpan struct {
Events []Event `json:"event" required:"true" nullable:"false"`
Attributes map[string]any `json:"attributes" required:"true" nullable:"false"`
Resource map[string]string `json:"resource" required:"true" nullable:"false"`
Scope Scope `json:"scope" required:"true" nullable:"false"`
Children []*FlamegraphSpan `json:"-"` // internal tree use only
}
@@ -67,13 +66,11 @@ func NewFlamegraphSpanFromStorable(s *StorableSpan, level int64, selectFields []
Events: s.UnmarshalledEvents(),
Attributes: make(map[string]any),
Resource: make(map[string]string),
Scope: newEmptyScope(),
}
if len(selectFields) == 0 {
return span
}
var attributes map[string]any
var scope Scope
for _, field := range selectFields {
switch field.FieldContext {
case telemetrytypes.FieldContextResource:
@@ -87,11 +84,6 @@ func NewFlamegraphSpanFromStorable(s *StorableSpan, level int64, selectFields []
if v, ok := attributes[field.Name]; ok {
span.Attributes[field.Name] = v
}
case telemetrytypes.FieldContextScope:
if scope.Attributes == nil {
scope = NewScope(s.Scope)
}
span.Scope.copyField(scope, field.Name)
}
}
return span

View File

@@ -15,7 +15,6 @@ func TestNewFlamegraphSpanFromStorable_SelectFields(t *testing.T) {
selectFields []telemetrytypes.TelemetryFieldKey
wantAttrs map[string]any
wantResource map[string]string
wantScope Scope
}{
{
name: "JSONColumn_ResolvesDottedPaths",
@@ -27,7 +26,6 @@ func TestNewFlamegraphSpanFromStorable_SelectFields(t *testing.T) {
},
wantAttrs: map[string]any{"http.route": "/orders", "http.retry.count": float64(2)},
wantResource: map[string]string{},
wantScope: newEmptyScope(),
},
{
name: "LegacyMaps_Resolve",
@@ -39,7 +37,6 @@ func TestNewFlamegraphSpanFromStorable_SelectFields(t *testing.T) {
},
wantAttrs: map[string]any{"http.route": "/cart", "cache.hit": true},
wantResource: map[string]string{},
wantScope: newEmptyScope(),
},
{
name: "ResourceFields_EmptyValueSkipped",
@@ -50,19 +47,6 @@ func TestNewFlamegraphSpanFromStorable_SelectFields(t *testing.T) {
},
wantAttrs: map[string]any{},
wantResource: map[string]string{"service.name": "checkout"},
wantScope: newEmptyScope(),
},
{
name: "ScopeFields_DeclaredPathsAndAttributes",
span: StorableSpan{Scope: scopeColumn("io.opentelemetry.http", "1.2.0", map[string]any{"lib.lang": "go", "team": "payments"})},
selectFields: []telemetrytypes.TelemetryFieldKey{
{Name: "scope.name", FieldContext: telemetrytypes.FieldContextScope},
{Name: "lib.lang", FieldContext: telemetrytypes.FieldContextScope},
{Name: "missing", FieldContext: telemetrytypes.FieldContextScope},
},
wantAttrs: map[string]any{},
wantResource: map[string]string{},
wantScope: Scope{Name: "io.opentelemetry.http", Attributes: map[string]any{"lib.lang": "go"}},
},
}
for _, testCase := range testCases {
@@ -70,7 +54,6 @@ func TestNewFlamegraphSpanFromStorable_SelectFields(t *testing.T) {
span := NewFlamegraphSpanFromStorable(&testCase.span, 0, testCase.selectFields)
assert.Equal(t, testCase.wantAttrs, span.Attributes)
assert.Equal(t, testCase.wantResource, span.Resource)
assert.Equal(t, testCase.wantScope, span.Scope)
})
}
}

View File

@@ -1,47 +0,0 @@
package spantypes
import (
"github.com/SigNoz/signoz/pkg/types/telemetrystoretypes"
)
const (
ScopeNameKey = "scope.name"
ScopeVersionKey = "scope.version"
)
// Scope is a span's instrumentation scope. Attributes are flat dotted keys, like the span attributes.
type Scope struct {
Name string `json:"name" required:"true"`
Version string `json:"version" required:"true"`
Attributes map[string]any `json:"attributes" required:"true" nullable:"false"`
}
func NewScope(column telemetrystoretypes.JSONValue) Scope {
scope := newEmptyScope()
fields := column.Flatten()
scope.Name, _ = fields["name"].(string)
scope.Version, _ = fields["version"].(string)
if attributes, ok := fields["attributes"].(map[string]any); ok {
scope.Attributes = attributes
}
return scope
}
func newEmptyScope() Scope {
return Scope{Attributes: make(map[string]any)}
}
// copyField copies one scope field key from src, resolved as the query builder does: scope.name and
// scope.version are the declared paths, any other name is a scope attribute.
func (s *Scope) copyField(src Scope, name string) {
switch name {
case ScopeNameKey:
s.Name = src.Name
case ScopeVersionKey:
s.Version = src.Version
default:
if v, ok := src.Attributes[name]; ok {
s.Attributes[name] = v
}
}
}

View File

@@ -1,53 +0,0 @@
package spantypes
import (
"testing"
"github.com/ClickHouse/clickhouse-go/v2/lib/chcol"
"github.com/stretchr/testify/assert"
"github.com/SigNoz/signoz/pkg/types/telemetrystoretypes"
)
// scopeColumn mirrors the driver's scan of the scope column: declared String paths as plain strings and the
// declared attributes path as a nested *chcol.JSON.
func scopeColumn(name, version string, attributes map[string]any) telemetrystoretypes.JSONValue {
j := chcol.NewJSON()
j.SetValueAtPath("name", name)
j.SetValueAtPath("version", version)
attrs := chcol.NewJSON()
for path, value := range attributes {
attrs.SetValueAtPath(path, value)
}
j.SetValueAtPath("attributes", attrs)
return telemetrystoretypes.NewJSONValue(*j)
}
func TestNewScope(t *testing.T) {
testCases := []struct {
name string
column telemetrystoretypes.JSONValue
want Scope
}{
{
name: "AttributesStayDotted",
column: scopeColumn("io.opentelemetry.http", "1.2.0", map[string]any{"lib.lang": "go", "lib.lang.version": "1.22", "retry": int64(3)}),
want: Scope{Name: "io.opentelemetry.http", Version: "1.2.0", Attributes: map[string]any{"lib.lang": "go", "lib.lang.version": "1.22", "retry": float64(3)}},
},
{
name: "DropsNullAttributePaths",
column: scopeColumn("checkout", "", map[string]any{"team": "payments", "absent": chcol.Variant{}}),
want: Scope{Name: "checkout", Attributes: map[string]any{"team": "payments"}},
},
{
name: "EmptyColumn",
column: telemetrystoretypes.JSONValue{},
want: Scope{Attributes: map[string]any{}},
},
}
for _, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) {
assert.Equal(t, testCase.want, NewScope(testCase.column))
})
}
}

View File

@@ -64,7 +64,6 @@ type WaterfallSpan struct {
Name string `json:"name"`
ParentSpanID string `json:"parent_span_id"`
Resource map[string]string `json:"resource"`
Scope Scope `json:"scope"`
SpanID string `json:"span_id"`
TimeUnix uint64 `json:"time_unix"`
TraceID string `json:"trace_id"`
@@ -114,7 +113,6 @@ type StorableSpan struct {
AttributesBool map[string]bool `ch:"attributes_bool"`
AttributesJSON telemetrystoretypes.JSONValue `ch:"attributes"`
ResourcesString map[string]string `ch:"resources_string"`
Scope telemetrystoretypes.JSONValue `ch:"scope"`
Events []string `ch:"events"`
StatusMessage string `ch:"status_message"`
StatusCodeString string `ch:"status_code_string"`
@@ -155,7 +153,6 @@ func (item *MinimalSpan) ToWaterfallSpan(traceID string) *WaterfallSpan {
HasError: item.HasError,
ServiceName: item.ServiceName,
Resource: map[string]string{"service.name": item.ServiceName},
Scope: newEmptyScope(),
Children: make([]*WaterfallSpan, 0),
Attributes: make(map[string]any),
Events: make([]Event, 0),
@@ -185,7 +182,6 @@ func NewMissingWaterfallSpan(spanID, traceID string, timeUnixNano, durationNano
Children: make([]*WaterfallSpan, 0),
Attributes: make(map[string]any),
Resource: make(map[string]string),
Scope: newEmptyScope(),
}
}
@@ -340,7 +336,6 @@ func (item *StorableSpan) ToWaterfallSpan(traceID string) *WaterfallSpan {
Name: item.Name,
ParentSpanID: item.ParentSpanID,
Resource: resources,
Scope: NewScope(item.Scope),
ResponseStatusCode: item.ResponseStatusCode,
SpanID: item.SpanID,
StatusCode: item.StatusCode,

View File

@@ -39,7 +39,7 @@ func (v JSONValue) Flatten() map[string]any {
if isNull(value) {
continue
}
flat[path] = flatObject(value)
flat[path] = value
}
return unwrap(flat)
}
@@ -48,11 +48,7 @@ func (v JSONValue) Flatten() map[string]any {
// A key stored at both a scalar and an object path collapses here, which Flatten avoids:
// https://github.com/SigNoz/events-pod/issues/8
func (v JSONValue) Nest() map[string]any {
nested := chcol.NewJSON()
for path, value := range v.column.ValuesByPath() {
nested.SetValueAtPath(path, flatObject(value))
}
return unwrap(nested.NestedMap())
return unwrap(v.column.NestedMap())
}
// MarshalJSON renders the flat attribute bag, so a reader that stores the scan target without calling
@@ -61,15 +57,6 @@ func (v JSONValue) MarshalJSON() ([]byte, error) {
return json.Marshal(v.Flatten())
}
// flatObject renders a declared JSON sub-path, which the driver scans as a nested chcol.JSON (such as
// scope.attributes), as a flat attribute bag.
func flatObject(value any) any {
if object, ok := value.(*chcol.JSON); ok && object != nil {
return JSONValue{column: *object}
}
return value
}
func isNull(value any) bool {
if value == nil {
return true

View File

@@ -50,7 +50,6 @@ ALL_SELECT_FIELDS = [
# all contextual columns (merged in response layer)
"attributes",
"resource",
"scope",
]

View File

@@ -545,7 +545,7 @@ def test_traces_list_response_shape_values(
now = datetime.now(tz=UTC).replace(microsecond=0)
trace_id = TraceIdGenerator.trace_id()
def span(marker: str, *, kind: TracesKind, status_code: TracesStatusCode = TracesStatusCode.STATUS_CODE_UNSET, status_message: str = "", flags: int = 0, attributes: dict[str, Any], scope: dict[str, Any] | None = None) -> Traces:
def span(marker: str, *, kind: TracesKind, status_code: TracesStatusCode = TracesStatusCode.STATUS_CODE_UNSET, status_message: str = "", flags: int = 0, attributes: dict[str, Any]) -> Traces:
return Traces(
timestamp=now - timedelta(seconds=1),
duration=timedelta(seconds=1),
@@ -558,11 +558,10 @@ def test_traces_list_response_shape_values(
flags=flags,
resources={"service.name": "shape-service", **extra_resources},
attributes={"marker": marker, **attributes, **extra_attrs},
scope=scope or {},
)
spans = [
span("error", kind=TracesKind.SPAN_KIND_SERVER, status_code=TracesStatusCode.STATUS_CODE_ERROR, status_message="boom", attributes={"str_a": "x", "num_a": 7.5, "bool_a": True}, scope={"name": "io.signoz.shape", "version": "0.1.0", "attributes": {"lib.lang": "go"}}),
span("error", kind=TracesKind.SPAN_KIND_SERVER, status_code=TracesStatusCode.STATUS_CODE_ERROR, status_message="boom", attributes={"str_a": "x", "num_a": 7.5, "bool_a": True}),
span("client", kind=TracesKind.SPAN_KIND_CLIENT, attributes={"http.url": "https://api.example.com/v1/orders", "http.request.method": "GET"}),
span("db", kind=TracesKind.SPAN_KIND_CLIENT, attributes={"db.name": "orders_db", "db.operation": "SELECT"}),
span("host", kind=TracesKind.SPAN_KIND_PRODUCER, attributes={"server.address": "payments.internal:8080"}),
@@ -623,7 +622,6 @@ def test_traces_list_response_shape_values(
"is_remote": span_obj.is_remote,
"attributes": {**span_obj.attribute_string, **span_obj.attributes_number, **span_obj.attributes_bool},
"resource": span_obj.resources_string,
"scope": span_obj.scope_json,
}
# `==` treats True/1 and 7.5/7.5 as equal regardless of type, so pin the
# string/number/bool type preservation for the error span explicitly.
@@ -1156,7 +1154,6 @@ def test_traces_list_span_scope(
"is_remote": x[3].is_remote,
"attributes": {**x[3].attribute_string, **x[3].attributes_number, **x[3].attributes_bool},
"resource": x[3].resources_string,
"scope": x[3].scope_json,
},
id="order_timestamp_full_shape",
),
@@ -1194,7 +1191,6 @@ def test_traces_list_span_scope(
"is_remote": x[3].is_remote,
"attributes": {**x[3].attribute_string, **x[3].attributes_number, **x[3].attributes_bool},
"resource": x[3].resources_string,
"scope": x[3].scope_json,
},
id="order_attribute_timestamp_full_shape",
),

View File

@@ -21,13 +21,12 @@ def test_waterfall_and_flamegraph_read_each_attribute_home(
trace_id = TraceIdGenerator.trace_id()
legacy_id, json_id, dual_id = (TraceIdGenerator.span_id() for _ in range(3))
resources = {"service.name": "tracedetail-attribute-homes"}
scope = {"name": "io.opentelemetry.http", "version": "1.2.0", "attributes": {"lib.lang": "go"}}
insert_traces(
[
Traces(timestamp=now - timedelta(seconds=10), duration=timedelta(seconds=9), trace_id=trace_id, span_id=legacy_id, name="GET /legacy", kind=TracesKind.SPAN_KIND_SERVER, resources=resources, attributes={"http.route": "/legacy", "http.retry.count": 1, "cache.hit": True}, attribute_write_mode="legacy_only"),
Traces(timestamp=now - timedelta(seconds=8), trace_id=trace_id, span_id=json_id, parent_span_id=legacy_id, name="db call", resources=resources, attributes={"http.route": "/json", "db.function": "node_refresh", "db.function.arg_count": 2}, attribute_write_mode="json_only"),
Traces(timestamp=now - timedelta(seconds=6), trace_id=trace_id, span_id=dual_id, parent_span_id=legacy_id, name="GET /dual", resources=resources, attributes={"http.route": "/dual"}, scope=scope),
Traces(timestamp=now - timedelta(seconds=6), trace_id=trace_id, span_id=dual_id, parent_span_id=legacy_id, name="GET /dual", resources=resources, attributes={"http.route": "/dual"}),
]
)
@@ -41,14 +40,11 @@ def test_waterfall_and_flamegraph_read_each_attribute_home(
timeout=10,
)
assert response.status_code == HTTPStatus.OK, response.text
spans = {span["span_id"]: span for span in response.json()["data"]["spans"]}
waterfall = {span_id: span["attributes"] for span_id, span in spans.items()}
waterfall = {span["span_id"]: span["attributes"] for span in response.json()["data"]["spans"]}
assert waterfall[legacy_id] == {"http.route": "/legacy", "http.retry.count": 1, "cache.hit": True}
# a key stored at both a scalar and an object path stays two flat keys
assert waterfall[json_id] == {"http.route": "/json", "db.function": "node_refresh", "db.function.arg_count": 2}
assert waterfall[dual_id] == {"http.route": "/dual"}
assert spans[dual_id]["scope"] == scope
assert spans[legacy_id]["scope"] == {"name": "", "version": "", "attributes": {}}
response = requests.post(
signoz.self.host_configs["8080"].get(f"/api/v3/traces/{trace_id}/flamegraph"),
@@ -58,8 +54,6 @@ def test_waterfall_and_flamegraph_read_each_attribute_home(
{"name": "http.route", "fieldContext": "attribute"},
{"name": "db.function.arg_count", "fieldContext": "attribute"},
{"name": "service.name", "fieldContext": "resource"},
{"name": "scope.name", "fieldContext": "scope"},
{"name": "lib.lang", "fieldContext": "scope"},
],
},
headers=headers,
@@ -71,5 +65,3 @@ def test_waterfall_and_flamegraph_read_each_attribute_home(
assert flamegraph[json_id]["attributes"] == {"http.route": "/json", "db.function.arg_count": 2}
assert flamegraph[dual_id]["attributes"] == {"http.route": "/dual"}
assert all(span["resource"] == resources for span in flamegraph.values())
assert flamegraph[dual_id]["scope"] == {"name": "io.opentelemetry.http", "version": "", "attributes": {"lib.lang": "go"}}
assert flamegraph[json_id]["scope"] == {"name": "", "version": "", "attributes": {}}