Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add aggregation filter operators #659

Merged
merged 2 commits into from
Jan 20, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,14 @@ import { type AstNode } from '@app-builder/models';
import {
type AggregationAstNode,
aggregationAstNodeName,
type AggregationFilterAstNode,
type AggregationFilterOperator,
type BinaryAggregationFilterOperator,
binaryAggregationFilterOperators,
type GetAggregationFilterOperator,
isUnaryAggregationFilter,
type UnaryAggregationFilterOperator,
unaryAggregationFilterOperators,
} from '@app-builder/models/astNode/aggregation';
import { NewConstantAstNode } from '@app-builder/models/astNode/constant';
import {
Expand Down Expand Up @@ -47,17 +55,44 @@ export interface AggregationViewModel {
aggregatedField: EvaluationError[];
};
}
export interface FilterViewModel {
operator: string | null;
export type FilterViewModel<
T extends AggregationFilterOperator = AggregationFilterOperator,
> = {
operator: T | null;
filteredField: DataModelField | null;
value: { astNode: AstNode; astNodeErrors?: AstNodeErrors };
errors: {
filter: EvaluationError[];
operator: EvaluationError[];
filteredField: EvaluationError[];
value: EvaluationError[];
};
}
} & (T extends UnaryAggregationFilterOperator
? { value?: undefined }
: {
value: { astNode: AstNode; astNodeErrors?: AstNodeErrors };
});

export const isUnaryFilterModel = (
filter: FilterViewModel,
): filter is FilterViewModel<UnaryAggregationFilterOperator> => {
return (
!!filter.operator &&
(
unaryAggregationFilterOperators as ReadonlyArray<AggregationFilterOperator>
).includes(filter.operator)
);
};

export const isBinaryFilterModel = (
filter: FilterViewModel,
): filter is FilterViewModel<BinaryAggregationFilterOperator> => {
return (
filter.operator === null ||
(
binaryAggregationFilterOperators as ReadonlyArray<AggregationFilterOperator>
).includes(filter.operator)
);
};

export const adaptAggregationViewModel = (
initialAggregationAstNode: AggregationAstNode,
Expand Down Expand Up @@ -114,20 +149,26 @@ export const adaptAggregationViewModel = (
};
};

function adaptFilterViewModel(
filterAstNode: AggregationAstNode['namedChildren']['filters']['children'][number],
function adaptFilterViewModel<
T extends AggregationFilterAstNode = AggregationFilterAstNode,
>(
filterAstNode: T,
initialAstNodeErrors: AstNodeErrors,
): FilterViewModel {
): FilterViewModel<GetAggregationFilterOperator<T>> {
return {
operator: filterAstNode.namedChildren.operator.constant,
filteredField: {
tableName: filterAstNode.namedChildren.tableName?.constant,
fieldName: filterAstNode.namedChildren.fieldName?.constant,
},
value: {
astNode: filterAstNode.namedChildren.value,
astNodeErrors: initialAstNodeErrors.namedChildren['value'],
},
...(isUnaryAggregationFilter(filterAstNode)
? {}
: {
value: {
astNode: filterAstNode.namedChildren.value,
astNodeErrors: initialAstNodeErrors.namedChildren['value'],
},
}),
errors: {
filter: initialAstNodeErrors.errors,
operator: computeValidationForNamedChildren(
Expand All @@ -140,36 +181,41 @@ function adaptFilterViewModel(
initialAstNodeErrors,
['tableName', 'fieldName'],
),
value: computeValidationForNamedChildren(
filterAstNode,
initialAstNodeErrors,
'value',
),
...(isUnaryAggregationFilter(filterAstNode)
? {}
: {
value: computeValidationForNamedChildren(
filterAstNode,
initialAstNodeErrors,
'value',
),
}),
},
};
} as FilterViewModel<GetAggregationFilterOperator<T>>;
}

export const adaptAggregationAstNode = (
aggregationViewModel: AggregationViewModel,
): AggregationAstNode => {
const filters = aggregationViewModel.filters.map(
(filter: FilterViewModel) => ({
name: 'Filter' as const,
constant: undefined,
children: [],
namedChildren: {
operator: NewConstantAstNode({
constant: filter.operator,
}),
tableName: NewConstantAstNode({
constant: filter.filteredField?.tableName ?? null,
}),
fieldName: NewConstantAstNode({
constant: filter.filteredField?.fieldName ?? null,
}),
value: filter.value.astNode,
},
}),
(filter: FilterViewModel) =>
({
name: 'Filter' as const,
constant: undefined,
children: [],
namedChildren: {
operator: NewConstantAstNode({
constant: filter.operator,
}),
tableName: NewConstantAstNode({
constant: filter.filteredField?.tableName ?? null,
}),
fieldName: NewConstantAstNode({
constant: filter.filteredField?.fieldName ?? null,
}),
value: filter.value?.astNode,
},
}) as AggregationFilterAstNode,
);
return {
name: aggregationAstNodeName,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ import { type AstNode, NewUndefinedAstNode } from '@app-builder/models';
import {
aggregationFilterOperators,
isAggregationFilterOperator,
} from '@app-builder/models/modale-operators';
isUnaryAggregationFilterOperator,
} from '@app-builder/models/astNode/aggregation';
import {
useDefaultCoerceToConstant,
useGetAstNodeOperandProps,
Expand All @@ -30,7 +31,7 @@ import { Icon } from 'ui-icons';

import { Operator } from '../../../../Operator';
import { Operand } from '../../../Operand';
import { type FilterViewModel } from './AggregationEdit';
import { type FilterViewModel, isBinaryFilterModel } from './AggregationEdit';
import { type DataModelField, EditDataModelField } from './EditDataModelField';

const newFilterValidation = () => ({
Expand Down Expand Up @@ -68,15 +69,46 @@ export function EditFilters({
filterIndex: number,
): void => {
onChange(
value.map((filter, index) =>
index === filterIndex
? {
...filter,
...newFieldValue,
validation: newFilterValidation(),
value.map((filter, index) => {
if (index !== filterIndex) {
return filter;
}

if ('operator' in newFieldValue && !!newFieldValue.operator) {
const isOldOpUnary = isUnaryAggregationFilterOperator(
filter.operator,
);
const isNewOpUnary = isUnaryAggregationFilterOperator(
newFieldValue.operator,
);
const isBinaryToUnary = !isOldOpUnary && isNewOpUnary;
const isUnaryToBinary = isOldOpUnary && !isNewOpUnary;

if (isBinaryToUnary || isUnaryToBinary) {
if (isBinaryToUnary) {
return {
operator: newFieldValue.operator,
filteredField: filter.filteredField,
value: undefined,
errors: newFilterValidation(),
};
} else {
return {
operator: newFieldValue.operator,
filteredField: filter.filteredField,
value: { astNode: NewUndefinedAstNode() },
errors: newFilterValidation(),
};
}
: filter,
),
}
}

return {
...filter,
...newFieldValue,
errors: newFilterValidation(),
};
}),
);
};

Expand Down Expand Up @@ -104,6 +136,8 @@ export function EditFilters({
{value.map((filter, filterIndex) => {
const isFirstCondition = filterIndex === 0;
const isLastCondition = filterIndex === value.length - 1;
const binaryFilter = isBinaryFilterModel(filter);

return (
<Fragment key={filterIndex}>
{/* Row 1 */}
Expand Down Expand Up @@ -152,21 +186,23 @@ export function EditFilters({
}
operators={aggregationFilterOperators}
/>
<FilterValue
filterValue={filter.value.astNode}
onSave={(astNode) =>
onFilterChange({ value: { astNode } }, filterIndex)
}
astNodeErrors={filter.value.astNodeErrors}
validationStatus={
filter.errors.value.length > 0 ? 'error' : 'valid'
}
/>
{binaryFilter ? (
<FilterValue
filterValue={filter.value.astNode}
onSave={(astNode) =>
onFilterChange({ value: { astNode } }, filterIndex)
}
astNodeErrors={filter.value.astNodeErrors}
validationStatus={
filter.errors.value.length > 0 ? 'error' : 'valid'
}
/>
) : null}
</div>
<EvaluationErrors
errors={adaptEvaluationErrorViewModels([
...filter.errors.filter,
...filter.errors.value,
...(isBinaryFilterModel(filter) ? filter.errors.value : []),
]).map(getNodeEvaluationErrorMessage)}
/>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
import {
type AggregationAstNode,
isAggregation,
isBinaryAggregationFilter,
} from '@app-builder/models/astNode/aggregation';
import {
type CustomListAccessAstNode,
Expand Down Expand Up @@ -225,7 +226,7 @@ function AggregatorDescription({ astNode }: { astNode: AggregationAstNode }) {
</span>
<span className="font-bold">{aggregatedFieldName}</span>
{filters.children.map((filter, index) => {
const { operator, fieldName, value } = filter.namedChildren;
const { operator, fieldName } = filter.namedChildren;
return (
<Fragment key={`filter_${index}`}>
<LogicalOperatorLabel
Expand All @@ -250,10 +251,12 @@ function AggregatorDescription({ astNode }: { astNode: AggregationAstNode }) {
operators={[operator?.constant as OperatorOption]}
viewOnly
/>
<OperandLabel
interactionMode="viewer"
{...getAstNodeOperandProps(value)}
/>
{isBinaryAggregationFilter(filter) ? (
<OperandLabel
interactionMode="viewer"
{...getAstNodeOperandProps(filter.namedChildren.value)}
/>
) : null}
</div>
</Fragment>
);
Expand Down
Loading
Loading