Skip to content

Commit

Permalink
feat(query-builder): add limit, order by and having clause to formula (
Browse files Browse the repository at this point in the history
…SigNoz#3623)

* feat: query builder formula is updated

* feat: formula is updated for having and limit

* feat: orderBy is updated

* feat: formula is added

* chore: add query-service support for formula limit and order by

* feat: enable more filters is displayed when all data source is metrics

* chore: feedback is updated

* chore: feedback is updated

---------

Co-authored-by: Srikanth Chekuri <srikanth.chekuri92@gmail.com>
Co-authored-by: Rajat Dabade <rajat@signoz.io>
  • Loading branch information
3 people authored and manishm committed Sep 27, 2023
1 parent c99ea18 commit 045ecb1
Show file tree
Hide file tree
Showing 34 changed files with 916 additions and 211 deletions.
20 changes: 19 additions & 1 deletion frontend/src/constants/queryBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ export const mapOfOperators = {
traces: tracesAggregateOperatorOptions,
};

export const mapOfFilters: Record<DataSource, QueryAdditionalFilter[]> = {
export const mapOfQueryFilters: Record<DataSource, QueryAdditionalFilter[]> = {
metrics: [
// eslint-disable-next-line sonarjs/no-duplicate-string
{ text: 'Aggregation interval', field: 'stepInterval' },
Expand All @@ -94,6 +94,24 @@ export const mapOfFilters: Record<DataSource, QueryAdditionalFilter[]> = {
],
};

const commonFormulaFilters: QueryAdditionalFilter[] = [
{
text: 'Having',
field: 'having',
},
{ text: 'Order by', field: 'orderBy' },
{ text: 'Limit', field: 'limit' },
];

export const mapOfFormulaToFilters: Record<
DataSource,
QueryAdditionalFilter[]
> = {
metrics: commonFormulaFilters,
logs: commonFormulaFilters,
traces: commonFormulaFilters,
};

export const REDUCE_TO_VALUES: SelectOption<ReduceOperators, string>[] = [
{ value: 'last', label: 'Latest of values in timeframe' },
{ value: 'sum', label: 'Sum of values in timeframe' },
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/container/LiveLogs/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import {
initialQueriesMap,
initialQueryBuilderFormValuesMap,
} from 'constants/queryBuilder';
import { FILTERS } from 'container/QueryBuilder/filters/OrderByFilter/config';
import { ORDERBY_FILTERS } from 'container/QueryBuilder/filters/OrderByFilter/config';
import {
BaseAutocompleteData,
DataTypes,
Expand All @@ -14,7 +14,7 @@ export const defaultLiveQueryDataConfig: Partial<IBuilderQuery> = {
aggregateOperator: LogsAggregatorOperator.NOOP,
disabled: true,
pageSize: 10,
orderBy: [{ columnName: 'timestamp', order: FILTERS.DESC }],
orderBy: [{ columnName: 'timestamp', order: ORDERBY_FILTERS.DESC }],
};

type GetDefaultCompositeQueryParams = {
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/container/LogsContextList/ShowButton.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Button, Typography } from 'antd';
import { FILTERS } from 'container/QueryBuilder/filters/OrderByFilter/config';
import { ORDERBY_FILTERS } from 'container/QueryBuilder/filters/OrderByFilter/config';

import { ShowButtonWrapper } from './styles';

Expand All @@ -19,7 +19,7 @@ function ShowButton({
return (
<ShowButtonWrapper>
<Typography>
Showing 10 lines {order === FILTERS.ASC ? 'after' : 'before'} match
Showing 10 lines {order === ORDERBY_FILTERS.ASC ? 'after' : 'before'} match
</Typography>
<Button
size="small"
Expand Down
12 changes: 6 additions & 6 deletions frontend/src/container/LogsContextList/index.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import RawLogView from 'components/Logs/RawLogView';
import Spinner from 'components/Spinner';
import { PANEL_TYPES } from 'constants/queryBuilder';
import { FILTERS } from 'container/QueryBuilder/filters/OrderByFilter/config';
import { ORDERBY_FILTERS } from 'container/QueryBuilder/filters/OrderByFilter/config';
import { useGetExplorerQueryRange } from 'hooks/queryBuilder/useGetExplorerQueryRange';
import { useIsDarkMode } from 'hooks/useDarkMode';
import { memo, useCallback, useEffect, useMemo, useState } from 'react';
Expand Down Expand Up @@ -87,7 +87,7 @@ function LogsContextList({
timestamp: item.timestamp,
}));

if (order === FILTERS.ASC) {
if (order === ORDERBY_FILTERS.ASC) {
const reversedCurrentLogs = currentLogs.reverse();
setLogs((prevLogs) => [...reversedCurrentLogs, ...prevLogs]);
} else {
Expand All @@ -111,7 +111,7 @@ function LogsContextList({
const handleShowNextLines = useCallback(() => {
if (isDisabledFetch) return;

const log = order === FILTERS.ASC ? firstLog : lastLog;
const log = order === ORDERBY_FILTERS.ASC ? firstLog : lastLog;

const newRequestData = getRequestData({
stagedQueryData: currentStagedQueryData,
Expand Down Expand Up @@ -167,7 +167,7 @@ function LogsContextList({

return (
<>
{order === FILTERS.ASC && (
{order === ORDERBY_FILTERS.ASC && (
<ShowButton
isLoading={isFetching}
isDisabled={isDisabledFetch}
Expand All @@ -186,11 +186,11 @@ function LogsContextList({
initialTopMostItemIndex={0}
data={logs}
itemContent={getItemContent}
followOutput={order === FILTERS.DESC}
followOutput={order === ORDERBY_FILTERS.DESC}
/>
</ListContainer>

{order === FILTERS.DESC && (
{order === ORDERBY_FILTERS.DESC && (
<ShowButton
isLoading={isFetching}
isDisabled={isDisabledFetch}
Expand Down
6 changes: 3 additions & 3 deletions frontend/src/container/LogsExplorerContext/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { Typography } from 'antd';
import Modal from 'antd/es/modal/Modal';
import RawLogView from 'components/Logs/RawLogView';
import LogsContextList from 'container/LogsContextList';
import { FILTERS } from 'container/QueryBuilder/filters/OrderByFilter/config';
import { ORDERBY_FILTERS } from 'container/QueryBuilder/filters/OrderByFilter/config';
import QueryBuilderSearch from 'container/QueryBuilder/filters/QueryBuilderSearch';
import { useIsDarkMode } from 'hooks/useDarkMode';
import { memo, useCallback, useState } from 'react';
Expand Down Expand Up @@ -87,7 +87,7 @@ function LogsExplorerContext({
/>
)}
<LogsContextList
order={FILTERS.ASC}
order={ORDERBY_FILTERS.ASC}
filters={filters}
isEdit={isEdit}
log={log}
Expand All @@ -103,7 +103,7 @@ function LogsExplorerContext({
/>
</LogContainer>
<LogsContextList
order={FILTERS.DESC}
order={ORDERBY_FILTERS.DESC}
filters={filters}
isEdit={isEdit}
log={log}
Expand Down
27 changes: 22 additions & 5 deletions frontend/src/container/QueryBuilder/QueryBuilder.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { MAX_FORMULAS, MAX_QUERIES } from 'constants/queryBuilder';
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
// ** Constants
import { memo, useEffect, useMemo } from 'react';
import { DataSource } from 'types/common/queryBuilder';

// ** Components
import { Formula, Query } from './components';
Expand Down Expand Up @@ -79,11 +80,27 @@ export const QueryBuilder = memo(function QueryBuilder({
/>
</Col>
))}
{currentQuery.builder.queryFormulas.map((formula, index) => (
<Col key={formula.queryName} span={24}>
<Formula formula={formula} index={index} />
</Col>
))}
{currentQuery.builder.queryFormulas.map((formula, index) => {
const isAllMetricDataSource = currentQuery.builder.queryData.every(
(query) => query.dataSource === DataSource.METRICS,
);

const query =
currentQuery.builder.queryData[index] ||
currentQuery.builder.queryData[0];

return (
<Col key={formula.queryName} span={24}>
<Formula
filterConfigs={filterConfigs}
query={query}
isAdditionalFilterEnable={isAllMetricDataSource}
formula={formula}
index={index}
/>
</Col>
);
})}
</Row>
</Col>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,13 @@ export const AdditionalFiltersToggler = memo(function AdditionalFiltersToggler({
setIsOpenedFilters((prevState) => !prevState);
};

const filtersTexts: ReactNode = listOfAdditionalFilter.map((str, index) => {
const filtersTexts: ReactNode = listOfAdditionalFilter?.map((str, index) => {
const isNextLast = index + 1 === listOfAdditionalFilter.length - 1;

if (index === listOfAdditionalFilter.length - 1) {
return (
<Fragment key={str}>
{listOfAdditionalFilter.length > 1 && 'and'}{' '}
{listOfAdditionalFilter?.length > 1 && 'and'}{' '}
<StyledLink>{str.toUpperCase()}</StyledLink>
</Fragment>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
import { IBuilderFormula } from 'types/api/queryBuilder/queryBuilderData';
import { QueryBuilderProps } from 'container/QueryBuilder/QueryBuilder.interfaces';
import {
IBuilderFormula,
IBuilderQuery,
} from 'types/api/queryBuilder/queryBuilderData';

export type FormulaProps = { formula: IBuilderFormula; index: number };
export type FormulaProps = {
formula: IBuilderFormula;
index: number;
query: IBuilderQuery;
filterConfigs: Partial<QueryBuilderProps['filterConfigs']>;
isAdditionalFilterEnable: boolean;
};
117 changes: 110 additions & 7 deletions frontend/src/container/QueryBuilder/components/Formula/Formula.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,45 @@
import { Col, Input } from 'antd';
import { Col, Input, Row } from 'antd';
// ** Components
import { ListItemWrapper, ListMarker } from 'container/QueryBuilder/components';
import {
FilterLabel,
ListItemWrapper,
ListMarker,
} from 'container/QueryBuilder/components';
import HavingFilter from 'container/QueryBuilder/filters/Formula/Having/HavingFilter';
import LimitFilter from 'container/QueryBuilder/filters/Formula/Limit/Limit';
import OrderByFilter from 'container/QueryBuilder/filters/Formula/OrderBy/OrderByFilter';
// ** Hooks
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
import { ChangeEvent, useCallback } from 'react';
import { useQueryOperations } from 'hooks/queryBuilder/useQueryBuilderOperations';
import { ChangeEvent, useCallback, useMemo } from 'react';
import { IBuilderFormula } from 'types/api/queryBuilder/queryBuilderData';

import { AdditionalFiltersToggler } from '../AdditionalFiltersToggler';
// ** Types
import { FormulaProps } from './Formula.interfaces';

const { TextArea } = Input;

export function Formula({ index, formula }: FormulaProps): JSX.Element {
export function Formula({
index,
formula,
filterConfigs,
query,
isAdditionalFilterEnable,
}: FormulaProps): JSX.Element {
const {
removeQueryBuilderEntityByIndex,
handleSetFormulaData,
} = useQueryBuilder();

const {
listOfAdditionalFormulaFilters,
handleChangeFormulaData,
} = useQueryOperations({
index,
query,
filterConfigs,
formula,
});

const handleDelete = useCallback(() => {
removeQueryBuilderEntityByIndex('queryFormulas', index);
}, [index, removeQueryBuilderEntityByIndex]);
Expand All @@ -43,6 +66,75 @@ export function Formula({ index, formula }: FormulaProps): JSX.Element {
[index, formula, handleSetFormulaData],
);

const handleChangeLimit = useCallback(
(value: IBuilderFormula['limit']) => {
handleChangeFormulaData('limit', value);
},
[handleChangeFormulaData],
);

const handleChangeHavingFilter = useCallback(
(value: IBuilderFormula['having']) => {
handleChangeFormulaData('having', value);
},
[handleChangeFormulaData],
);

const handleChangeOrderByFilter = useCallback(
(value: IBuilderFormula['orderBy']) => {
handleChangeFormulaData('orderBy', value);
},
[handleChangeFormulaData],
);

const renderAdditionalFilters = useMemo(
() => (
<>
<Col span={11}>
<Row gutter={[11, 5]}>
<Col flex="5.93rem">
<FilterLabel label="Limit" />
</Col>
<Col flex="1 1 12.5rem">
<LimitFilter formula={formula} onChange={handleChangeLimit} />
</Col>
</Row>
</Col>
<Col span={11}>
<Row gutter={[11, 5]}>
<Col flex="5.93rem">
<FilterLabel label="HAVING" />
</Col>
<Col flex="1 1 12.5rem">
<HavingFilter formula={formula} onChange={handleChangeHavingFilter} />
</Col>
</Row>
</Col>
<Col span={11}>
<Row gutter={[11, 5]}>
<Col flex="5.93rem">
<FilterLabel label="Order by" />
</Col>
<Col flex="1 1 12.5rem">
<OrderByFilter
query={query}
formula={formula}
onChange={handleChangeOrderByFilter}
/>
</Col>
</Row>
</Col>
</>
),
[
formula,
handleChangeHavingFilter,
handleChangeLimit,
handleChangeOrderByFilter,
query,
],
);

return (
<ListItemWrapper onDelete={handleDelete}>
<Col span={24}>
Expand All @@ -54,7 +146,7 @@ export function Formula({ index, formula }: FormulaProps): JSX.Element {
/>
</Col>
<Col span={24}>
<TextArea
<Input.TextArea
name="expression"
onChange={handleChange}
size="middle"
Expand All @@ -71,6 +163,17 @@ export function Formula({ index, formula }: FormulaProps): JSX.Element {
addonBefore="Legend Format"
/>
</Col>
{isAdditionalFilterEnable && (
<Col span={24}>
<AdditionalFiltersToggler
listOfAdditionalFilter={listOfAdditionalFormulaFilters}
>
<Row gutter={[0, 11]} justify="space-between">
{renderAdditionalFilters}
</Row>
</AdditionalFiltersToggler>
</Col>
)}
</ListItemWrapper>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import AggregateEveryFilter from 'container/QueryBuilder/filters/AggregateEveryF
import LimitFilter from 'container/QueryBuilder/filters/LimitFilter/LimitFilter';
import QueryBuilderSearch from 'container/QueryBuilder/filters/QueryBuilderSearch';
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
import { useQueryOperations } from 'hooks/queryBuilder/useQueryOperations';
import { useQueryOperations } from 'hooks/queryBuilder/useQueryBuilderOperations';
// ** Hooks
import { ChangeEvent, memo, ReactNode, useCallback } from 'react';
import { IBuilderQuery } from 'types/api/queryBuilder/queryBuilderData';
Expand Down
Loading

0 comments on commit 045ecb1

Please sign in to comment.