add sort option to GroupBy query builder

This commit is contained in:
Stephanie Yang 2021-06-30 13:48:23 -05:00
parent 383b0a69d1
commit 46016fce4a
5 changed files with 188 additions and 6 deletions

View file

@ -0,0 +1,8 @@
.sortSelectRow {
margin-top: 15px;
display: flex;
.fieldSelect {
margin-left: 15px;
}
}

View file

@ -0,0 +1,118 @@
import React, { FC } from 'react';
import { Select } from 'shared/Select';
import css from './GroupBySort.module.scss';
export type SortOption = {
sortValue: string;
field?: string;
};
type GroupBySortProps = {
sort: SortOption[];
fields: { label: string; value: string }[];
showErrors: boolean;
onUpdate: (sort: SortOption[]) => void;
};
export const GroupBySort: FC<GroupBySortProps> = ({
sort,
fields,
showErrors,
onUpdate
}) => {
const hasPrimary = sort.length > 0;
const primary = hasPrimary ? sort[0].sortValue.split(' ')[0] : '';
const hasSecondary = sort.length > 1;
const secondary = hasSecondary ? sort[1].sortValue.split(' ')[0] : '';
const sortOptions = [
{ label: 'Count (desc)', value: 'count desc' },
{ label: 'Count (asc)', value: 'count asc' },
{ label: 'Sum (desc)', value: 'sum desc' },
{ label: 'Sum (asc)', value: 'sum asc' }
];
const onPrimaryChange = (value: string) => {
const split = value.split(' ');
const isSum = split[0] === 'sum';
const fieldValue = hasPrimary ? sort[0].field : undefined;
if (!hasSecondary) {
onUpdate([{ sortValue: value, field: isSum ? fieldValue : undefined }]);
} else if (hasSecondary && sort[1].sortValue.includes(split[0])) {
onUpdate([{ sortValue: value, field: isSum ? fieldValue : undefined }]);
} else {
onUpdate([
{ sortValue: value, field: isSum ? fieldValue : undefined },
sort[1]
]);
}
};
const onSecondaryChange = (value: string) => {
let sortOp = { sortValue: value };
if (hasSecondary) {
sortOp = { ...sort[1], ...sortOp };
}
onUpdate([sort[0], sortOp]);
};
const onUpdateSumField = (isPrimary: boolean, value: string) => {
let clone = [...sort];
if (isPrimary) {
clone[0].field = value;
} else {
clone[1].field = value;
}
onUpdate(clone);
};
return (
<div>
<div className={css.sortSelectRow}>
<Select
label="Primary Sort"
value={hasPrimary ? sort[0].sortValue : ''}
options={sortOptions}
onChange={(value) => onPrimaryChange(value)}
/>
{primary.includes('sum') ? (
<Select
className={css.fieldSelect}
label="Field"
value={sort[0].field ? sort[0].field : ''}
options={fields}
onChange={(value) => onUpdateSumField(true, value)}
error={showErrors ? !sort[0].field : false}
/>
) : null}
</div>
{hasPrimary ? (
<div className={css.sortSelectRow}>
<Select
label="Secondary Sort"
value={hasSecondary ? sort[1].sortValue : ''}
options={sortOptions.filter(
(option) => !option.value.includes(primary)
)}
onChange={(value) => onSecondaryChange(value)}
/>
{secondary.includes('sum') ? (
<Select
className={css.fieldSelect}
label="Field"
value={sort[0].field ? sort[0].field : ''}
options={fields}
onChange={(value) => onUpdateSumField(false, value)}
error={showErrors ? !sort[1].field : false}
/>
) : null}
</div>
) : null}
</div>
);
};

View file

@ -0,0 +1 @@
export * from './GroupBySort';

View file

@ -30,6 +30,7 @@ import { ResultType } from 'App/Query/QueryContainer';
import { RowCall } from './RowCall';
import { SavedQueries } from './SavedQueries';
import { Select } from 'shared/Select';
import { GroupBySort, SortOption } from './GroupBySort';
import { stringifyRowData } from './stringifyRowData';
import css from './QueryBuilder.module.scss';
@ -47,7 +48,12 @@ type QueryBuilderProps = {
columns: string[],
operator?: Operator
) => void;
onRunGroupBy: (table: any, rowsData: RowsCallType, filter?: string) => void;
onRunGroupBy: (
table: any,
rowsData: RowsCallType,
sort: SortOption[],
filter?: string
) => void;
onExternalLookup: (table: string, columns: number[]) => void;
onClear: () => void;
};
@ -95,6 +101,7 @@ export const QueryBuilder: FC<QueryBuilderProps> = ({
);
const [groupByFilters, setGroupByFilters] = useState<any[]>([]);
const [filter, setFilter] = useState<string>();
const [sort, setSort] = useState<SortOption[]>([]);
const colSizes = JSON.parse(
localStorage.getItem('builderColSizes') || '[25, 75]'
);
@ -231,10 +238,25 @@ export const QueryBuilder: FC<QueryBuilderProps> = ({
const onRunClick = () => {
if (operation === 'GroupBy') {
const isInvalid = groupByCall.primary ? false : true;
let isInvalid = false;
if (!groupByCall.primary) {
isInvalid = true;
} else if (
sort.length > 0 &&
sort[0].sortValue.includes('sum') &&
!sort[0].field
) {
isInvalid = true;
} else if (
sort.length > 1 &&
sort[1].sortValue.includes('sum') &&
!sort[1].field
) {
isInvalid = true;
}
setHasInvalid(isInvalid);
if (!isInvalid) {
onRunGroupBy(selectedTable, groupByCall, filter);
onRunGroupBy(selectedTable, groupByCall, sort, filter);
}
} else {
const { cleanRowCalls, isInvalid } = cleanupRows();
@ -328,7 +350,7 @@ export const QueryBuilder: FC<QueryBuilderProps> = ({
if (!isInvalid) {
if (operation === 'GroupBy') {
onRunGroupBy(tableDetails, groupByCall, filter);
onRunGroupBy(tableDetails, groupByCall, sort, filter);
} else {
onQuery(tableDetails, operation, rowCalls, columns, operator);
}
@ -659,6 +681,22 @@ export const QueryBuilder: FC<QueryBuilderProps> = ({
for {selectedTable.name} with at least one field constraint.
</div>
)}
<div className={css.sortHeader}>
<Typography variant="caption" color="textSecondary">
Sort (optional)
</Typography>
<GroupBySort
sort={sort}
onUpdate={setSort}
fields={selectedTable.fields
.filter((field) => field.options.type === 'int')
.map((field) => {
return { label: field.name, value: field.name };
})}
showErrors={hasInvalid}
/>
</div>
</div>
) : (
<div className={css.newGroup}>

View file

@ -13,6 +13,7 @@ import { ResultType } from 'App/Query/QueryContainer';
import { queryPQL } from 'services/grpcServices';
import { grpc } from '@improbable-eng/grpc-web';
import { RowResponse } from 'proto/pilosa_pb';
import { SortOption } from './GroupBySort';
import { stringifyRowData } from './stringifyRowData';
import css from './QueryBuilderContainer.module.scss';
@ -160,6 +161,7 @@ export const QueryBuilderContainer = () => {
const onRunGroupBy = (
table: any,
rowsData: RowsCallType,
sort: SortOption[],
filter?: string
) => {
streamingResults = {
@ -174,11 +176,26 @@ export const QueryBuilderContainer = () => {
};
startTime = moment();
setLoading(true);
let sortString = '';
let aggregateString = '';
if (sort.length > 0) {
sortString = sort[0].sortValue;
if (sort[0].sortValue.includes('sum')) {
aggregateString = `, aggregate=Sum(field=${sort[0].field})`;
}
if (sort.length > 1) {
sortString = `${sortString}, ${sort[1].sortValue}`;
if (sort[1].sortValue.includes('sum')) {
aggregateString = `, aggregate=Sum(field=${sort[1].field})`;
}
}
sortString = `, sort="${sortString}"`;
}
const filterString =
filter && filter.length > 0 ? `, filter=${filter}` : '';
const query = rowsData.secondary
? `GroupBy(Rows(${rowsData.primary}), Rows(${rowsData.secondary})${filterString})`
: `GroupBy(Rows(${rowsData.primary})${filterString})`;
? `GroupBy(Rows(${rowsData.primary}), Rows(${rowsData.secondary})${filterString}${sortString}${aggregateString})`
: `GroupBy(Rows(${rowsData.primary})${filterString}${sortString}${aggregateString})`;
streamingResults.query = query;
queryPQL(table.name, query, handleQueryMessages, handleQueryEnd);
};