Skip to content
This repository was archived by the owner on May 13, 2025. It is now read-only.

Commit 8fa09a8

Browse files
authored
Added filter_query to filter builder (#355)
This PR ensures that console adds the SQL query equivalent to the filter user has added. This is required for other clients like `pb` to work properly.
1 parent ce3c5e6 commit 8fa09a8

File tree

7 files changed

+81
-32
lines changed

7 files changed

+81
-32
lines changed

src/@types/parseable/api/savedFilters.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,7 @@ export type CreateSavedFilterType = {
2121
filter_name: string;
2222
query: {
2323
filter_type: 'sql' | 'builder';
24-
filter_query?: string;
25-
filter_builder?: QueryType;
24+
filter_query: string;
2625
};
2726
time_filter: null | {
2827
from: string;

src/hooks/useSavedFilters.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,13 @@ const useSavedFiltersQuery = () => {
2626
);
2727

2828
const { mutate: updateSavedFilters, isLoading: isUpdating } = useMutation(
29-
(data: { filter: SavedFilterType; onSuccess?: () => void }) => putSavedFilters(data.filter.filter_id, data.filter),
29+
(data: { filter: SavedFilterType; onSuccess?: () => void }) => {
30+
// filter_builder will be deleted only for new filters.
31+
if (_.has(data.filter.query, 'filter_builder')) {
32+
data.filter.query = _.omit(data.filter.query, 'filter_builder');
33+
}
34+
return putSavedFilters(data.filter.filter_id, data.filter);
35+
},
3036
{
3137
onSuccess: (_data, variables) => {
3238
variables.onSuccess && variables.onSuccess();

src/pages/Stream/components/Querier/SaveFilterModal.tsx

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,7 @@ interface FormObjectType extends Omit<SavedFilterType, 'filter_id' | 'version'>
2222
}
2323

2424
const sanitizeFilterItem = (formObject: FormObjectType): SavedFilterType => {
25-
const {
26-
stream_name,
27-
filter_name,
28-
filter_id = '',
29-
query,
30-
selectedTimeRangeOption,
31-
version = '',
32-
} = formObject;
25+
const { stream_name, filter_name, filter_id = '', query, selectedTimeRangeOption, version = '' } = formObject;
3326
return {
3427
filter_id,
3528
version,
@@ -42,7 +35,6 @@ const sanitizeFilterItem = (formObject: FormObjectType): SavedFilterType => {
4235

4336
const SaveFilterModal = () => {
4437
const [isSaveFiltersModalOpen, setFilterStore] = useFilterStore((store) => store.isSaveFiltersModalOpen);
45-
const [appliedQuery] = useFilterStore((store) => store.appliedQuery);
4638
const [activeSavedFilters] = useAppStore((store) => store.activeSavedFilters);
4739
const [formObject, setFormObject] = useState<FormObjectType | null>(null);
4840
const [currentStream] = useAppStore((store) => store.currentStream);
@@ -76,7 +68,7 @@ const SaveFilterModal = () => {
7668
filter_name: '',
7769
query: {
7870
filter_type: isSqlMode ? 'sql' : 'builder',
79-
...(isSqlMode ? { filter_query: custSearchQuery } : { filter_builder: appliedQuery }),
71+
filter_query: custSearchQuery,
8072
},
8173
time_filter: {
8274
from: timeRange.startTime.toISOString(),

src/pages/Stream/components/Querier/SavedFiltersModal.tsx

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import classes from './styles/SavedFiltersModalStyles.module.css';
1212
import { EmptySimple } from '@/components/Empty';
1313
import { useAppStore } from '@/layouts/MainLayout/providers/AppProvider';
1414
import useSavedFiltersQuery from '@/hooks/useSavedFilters';
15+
import { generateQueryBuilderASTFromSQL } from '../../utils';
1516

1617
const { toggleSavedFiltersModal, resetFilters, parseQuery, applySavedFilters } = filterStoreReducers;
1718
const { applyCustomQuery, updateSavedFilterId, getCleanStoreForRefetch, setTimeRange } = logsStoreReducers;
@@ -68,21 +69,30 @@ const SavedFilterItem = (props: {
6869
deleteSavedFilterMutation({ filter_id });
6970
}, [showDeletePropmt]);
7071

72+
const handleTimeFilter = useCallback(() => {
73+
if (time_filter === null || (time_filter && isStoredAndCurrentTimeRangeAreSame(time_filter.from, time_filter.to))) {
74+
hardRefresh();
75+
} else {
76+
changeTimerange(time_filter.from, time_filter.to);
77+
}
78+
}, [time_filter, isStoredAndCurrentTimeRangeAreSame, hardRefresh, changeTimerange]);
79+
7180
const onApplyFilters = useCallback(() => {
72-
if (_.isString(query.filter_query)) {
73-
props.onSqlSearchApply(query.filter_query, filter_id, time_filter);
81+
if (query.filter_query) {
82+
if (query.filter_type === 'sql') {
83+
props.onSqlSearchApply(query.filter_query, filter_id, time_filter);
84+
} else {
85+
if (filter_id !== savedFilterId) {
86+
props.onFilterBuilderQueryApply(generateQueryBuilderASTFromSQL(query.filter_query), filter_id);
87+
} else {
88+
handleTimeFilter();
89+
}
90+
}
7491
} else if (query.filter_builder) {
7592
if (filter_id !== savedFilterId) {
7693
props.onFilterBuilderQueryApply(query.filter_builder, filter_id);
7794
} else {
78-
if (
79-
time_filter === null ||
80-
(time_filter && isStoredAndCurrentTimeRangeAreSame(time_filter.from, time_filter.to))
81-
) {
82-
hardRefresh();
83-
} else {
84-
changeTimerange(time_filter.from, time_filter.to);
85-
}
95+
handleTimeFilter();
8696
}
8797
}
8898
}, [savedFilterId, isStoredAndCurrentTimeRangeAreSame, hardRefresh, changeTimerange]);

src/pages/Stream/hooks/useParamsController.ts

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,14 @@ import { FIXED_DURATIONS } from '@/constants/timeConstants';
66
import dayjs from 'dayjs';
77
import timeRangeUtils from '@/utils/timeRangeUtils';
88
import moment from 'moment-timezone';
9+
import { filterStoreReducers, QueryType, useFilterStore } from '../providers/FilterProvider';
10+
import { generateQueryBuilderASTFromSQL } from '../utils';
911

1012
const { getRelativeStartAndEndDate, formatDateWithTimezone, getLocalTimezone } = timeRangeUtils;
1113
const { setTimeRange, onToggleView, setPerPage, setCustQuerySearchState } = logsStoreReducers;
14+
const { applySavedFilters } = filterStoreReducers;
1215
const timeRangeFormat = 'DD-MMM-YYYY_HH-mmz';
13-
const keys = ['view', 'rows', 'interval', 'from', 'to', 'query'];
16+
const keys = ['view', 'rows', 'interval', 'from', 'to', 'query', 'filterType'];
1417
const FIXED_ROWS = ['50', '100', '150', '200'];
1518

1619
const dateToParamString = (date: Date) => {
@@ -54,15 +57,17 @@ const storeToParamsObj = (opts: {
5457
page: string;
5558
rows: string;
5659
query: string;
60+
filterType: string;
5761
}): Record<string, string> => {
58-
const { timeRange, offset, page, view, rows, query } = opts;
62+
const { timeRange, offset, page, view, rows, query, filterType } = opts;
5963
const params: Record<string, string> = {
6064
...deriveTimeRangeParams(timeRange),
6165
view,
6266
offset,
6367
rows,
6468
page,
6569
query,
70+
filterType: query ? filterType : '',
6671
};
6772
return _.pickBy(params, (val, key) => !_.isEmpty(val) && _.includes(keys, key));
6873
};
@@ -84,6 +89,7 @@ const useParamsController = () => {
8489
const [viewMode] = useLogsStore((store) => store.viewMode);
8590
const [custQuerySearchState] = useLogsStore((store) => store.custQuerySearchState);
8691
const [timeRange, setLogsStore] = useLogsStore((store) => store.timeRange);
92+
const [, setFilterStore] = useFilterStore((store) => store);
8793

8894
const { currentOffset, currentPage, perPage } = tableOpts;
8995

@@ -97,6 +103,7 @@ const useParamsController = () => {
97103
view: viewMode,
98104
rows: `${perPage}`,
99105
query: custQuerySearchState.custSearchQuery,
106+
filterType: custQuerySearchState.viewMode,
100107
});
101108
const presentParams = paramsStringToParamsObj(searchParams);
102109
if (['table', 'json'].includes(presentParams.view) && presentParams.view !== storeAsParams.view) {
@@ -107,7 +114,11 @@ const useParamsController = () => {
107114
}
108115

109116
if (storeAsParams.query !== presentParams.query) {
110-
setLogsStore((store) => setCustQuerySearchState(store, presentParams.query));
117+
setLogsStore((store) => setCustQuerySearchState(store, presentParams.query, presentParams.filterType));
118+
if (presentParams.filterType === 'filters')
119+
setFilterStore((store) =>
120+
applySavedFilters(store, generateQueryBuilderASTFromSQL(presentParams.query) as QueryType),
121+
);
111122
}
112123
syncTimeRangeToStore(storeAsParams, presentParams);
113124
setStoreSynced(true);
@@ -122,6 +133,7 @@ const useParamsController = () => {
122133
view: viewMode,
123134
rows: `${perPage}`,
124135
query: custQuerySearchState.custSearchQuery,
136+
filterType: custQuerySearchState.viewMode,
125137
});
126138
const presentParams = paramsStringToParamsObj(searchParams);
127139
if (_.isEqual(storeAsParams, presentParams)) return;
@@ -139,6 +151,7 @@ const useParamsController = () => {
139151
view: viewMode,
140152
rows: `${perPage}`,
141153
query: custQuerySearchState.custSearchQuery,
154+
filterType: custQuerySearchState.viewMode,
142155
});
143156
const presentParams = paramsStringToParamsObj(searchParams);
144157

@@ -152,8 +165,12 @@ const useParamsController = () => {
152165
setLogsStore((store) => setPerPage(store, _.toNumber(presentParams.rows)));
153166
}
154167

155-
if (storeAsParams.query !== presentParams.query) {
156-
setLogsStore((store) => setCustQuerySearchState(store, presentParams.query));
168+
if (storeAsParams.query !== presentParams.query && !_.isEmpty(presentParams.query)) {
169+
if (presentParams.filterType === 'filters')
170+
setFilterStore((store) =>
171+
applySavedFilters(store, generateQueryBuilderASTFromSQL(presentParams.query) as QueryType),
172+
);
173+
setLogsStore((store) => setCustQuerySearchState(store, presentParams.query, presentParams.filterType));
157174
}
158175
syncTimeRangeToStore(storeAsParams, presentParams);
159176
}, [searchParams]);

src/pages/Stream/providers/LogsProvider.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -248,7 +248,7 @@ type LogsStoreReducers = {
248248
resetQuickFilters: (store: LogsStore) => ReducerOutput;
249249
streamChangeCleanup: (store: LogsStore) => ReducerOutput;
250250
toggleQueryBuilder: (store: LogsStore, val?: boolean) => ReducerOutput;
251-
setCustQuerySearchState: (store: LogsStore, query: string) => ReducerOutput;
251+
setCustQuerySearchState: (store: LogsStore, query: string, viewMode: string) => ReducerOutput;
252252
resetCustQuerySearchState: (store: LogsStore) => ReducerOutput;
253253
toggleCustQuerySearchViewMode: (store: LogsStore, targetMode: 'sql' | 'filters') => ReducerOutput;
254254
toggleDeleteModal: (store: LogsStore, val?: boolean) => ReducerOutput;
@@ -455,16 +455,16 @@ const toggleQueryBuilder = (store: LogsStore, val?: boolean) => {
455455
};
456456
};
457457

458-
const setCustQuerySearchState = (store: LogsStore, query: string) => {
458+
const setCustQuerySearchState = (store: LogsStore, query: string, viewMode: string) => {
459459
const { timeRange } = store;
460460
return {
461461
custQuerySearchState: {
462462
showQueryBuilder: false,
463463
savedFilterId: null,
464464
isQuerySearchActive: true,
465465
custSearchQuery: query,
466-
viewMode: 'sql',
467-
activeMode: 'sql' as 'sql',
466+
viewMode,
467+
activeMode: viewMode === 'filters' ? ('filters' as 'filters') : ('sql' as 'sql'),
468468
},
469469
...getCleanStoreForRefetch(store),
470470
timeRange,

src/pages/Stream/utils.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,38 @@
11
import { Log } from '@/@types/parseable/api/query';
22
import { LogStreamSchemaData } from '@/@types/parseable/api/stream';
33
import { columnsToSkip } from './providers/LogsProvider';
4+
import { parseSQL } from 'react-querybuilder';
5+
import { QueryType, RuleGroupTypeOverride, RuleTypeOverride } from './providers/FilterProvider';
46

57
export const getPageSlice = (page = 1, perPage: number, data: Log[]) => {
68
const firstPageIndex = (page - 1) * perPage;
79
const lastPageIndex = firstPageIndex + perPage;
810
return data ? data.slice(firstPageIndex, lastPageIndex) : [];
911
};
1012

13+
export const generateQueryBuilderASTFromSQL = (sqlString: string) => {
14+
const parsedQuery = parseSQL(sqlString) as QueryType;
15+
16+
function isRuleGroup(rule: RuleTypeOverride | RuleGroupTypeOverride): rule is RuleGroupTypeOverride {
17+
return 'combinator' in rule && 'rules' in rule;
18+
}
19+
20+
function addIds(query: QueryType | RuleGroupTypeOverride) {
21+
if (Array.isArray(query.rules)) {
22+
query.rules.forEach((rule) => {
23+
rule.id = `rule-${Math.random()}`;
24+
25+
if (isRuleGroup(rule)) {
26+
addIds(rule);
27+
}
28+
});
29+
}
30+
}
31+
32+
addIds(parsedQuery);
33+
return parsedQuery;
34+
};
35+
1136
export const makeHeadersFromSchema = (schema: LogStreamSchemaData | null): string[] => {
1237
if (schema) {
1338
const { fields } = schema;

0 commit comments

Comments
 (0)