Skip to content

Commit

Permalink
string filter queries (#1028)
Browse files Browse the repository at this point in the history
* string filter queries

* remove viewDefinitionVersion test

* remove old files
  • Loading branch information
adrianmroz-allegro committed Feb 13, 2023
1 parent bf46e7b commit 4df5eb4
Show file tree
Hide file tree
Showing 9 changed files with 149 additions and 175 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,12 @@ import {
StringFilterClause
} from "../../../common/models/filter-clause/filter-clause";
import { FilterMode } from "../../../common/models/filter/filter";
import { Timekeeper } from "../../../common/models/timekeeper/timekeeper";
import { debounceWithPromise, Unary } from "../../../common/utils/functional/functional";
import { Fn } from "../../../common/utils/general/general";
import { previewStringFilterQuery } from "../../../common/utils/query/preview-string-filter-query";
import { SEARCH_WAIT, STRINGS } from "../../config/constants";
import { classNames, enterKey } from "../../utils/dom/dom";
import { reportError } from "../../utils/error-reporter/error-reporter";
import { ApiContext, ApiContextValue } from "../../views/cube-view/api-context";
import { Button } from "../button/button";
import { ClearableInput } from "../clearable-input/clearable-input";
import { GlobalEventListener } from "../global-event-listener/global-event-listener";
Expand All @@ -66,7 +65,6 @@ export type PreviewFilterMode = FilterMode.CONTAINS | FilterMode.REGEX;
export interface PreviewStringFilterMenuProps {
dimension: Dimension;
essence: Essence;
timekeeper: Timekeeper;
onClose: Fn;
filterMode: FilterMode.REGEX | FilterMode.CONTAINS;
saveClause: Unary<FilterClause, void>;
Expand All @@ -77,14 +75,11 @@ export interface PreviewStringFilterMenuState {
dataset: DatasetRequest;
}

interface QueryProps {
essence: Essence;
timekeeper: Timekeeper;
dimension: Dimension;
filterMode: PreviewFilterMode;
}

export class PreviewStringFilterMenu extends React.Component<PreviewStringFilterMenuProps, PreviewStringFilterMenuState> {
static contextType = ApiContext;

context: ApiContextValue;

private lastSearchText: string;

initialSearchText = (): string => {
Expand Down Expand Up @@ -115,7 +110,7 @@ export class PreviewStringFilterMenu extends React.Component<PreviewStringFilter
private sendQueryFilter(): Promise<DatasetRequest> {
const { searchText } = this.state;
this.lastSearchText = searchText;
return this.debouncedQueryFilter({ ...this.props, searchText });
return this.debouncedQueryFilter(this.props.essence, this.constructClause());
}

private regexErrorMessage(): string {
Expand All @@ -124,12 +119,11 @@ export class PreviewStringFilterMenu extends React.Component<PreviewStringFilter
return filterMode === FilterMode.REGEX && searchText && checkRegex(searchText);
}

private queryFilter = (props: QueryProps): Promise<DatasetRequest> => {
const { essence } = props;
private queryFilter = (essence: Essence, clause: StringFilterClause): Promise<DatasetRequest> => {
const { stringFilterQuery } = this.context;
const { searchText } = this.state;
const query = previewStringFilterQuery({ ...props, searchText, limit: TOP_N + 1 });

return essence.dataCube.executor(query, { timezone: essence.timezone })
return stringFilterQuery(essence, clause)
.then((dataset: Dataset) => {
if (this.lastSearchText !== searchText) return null;
return loaded(dataset);
Expand Down Expand Up @@ -164,10 +158,9 @@ export class PreviewStringFilterMenu extends React.Component<PreviewStringFilter
}
};

constructClause(): FilterClause {
constructClause(): StringFilterClause {
const { dimension, filterMode } = this.props;
const { searchText } = this.state;
if (!searchText) return null;
const { name: reference } = dimension;

switch (filterMode) {
Expand Down Expand Up @@ -201,8 +194,11 @@ export class PreviewStringFilterMenu extends React.Component<PreviewStringFilter
const { essence: { filter }, dimension } = this.props;
if (this.regexErrorMessage()) return false;
const newClause = this.constructClause();
if (!newClause.values.first()) {
return false;
}
const oldClause = filter.getClauseForDimension(dimension);
return newClause && !newClause.equals(oldClause);
return !newClause.equals(oldClause);
}

render() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,10 @@ import { FilterMode } from "../../../common/models/filter/filter";
import { Timekeeper } from "../../../common/models/timekeeper/timekeeper";
import { debounceWithPromise, Unary } from "../../../common/utils/functional/functional";
import { Fn } from "../../../common/utils/general/general";
import { stringFilterOptionsQuery } from "../../../common/utils/query/selectable-string-filter-query";
import { SEARCH_WAIT, STRINGS } from "../../config/constants";
import { classNames, enterKey } from "../../utils/dom/dom";
import { reportError } from "../../utils/error-reporter/error-reporter";
import { ApiContext, ApiContextValue } from "../../views/cube-view/api-context";
import { Button } from "../button/button";
import { ClearableInput } from "../clearable-input/clearable-input";
import { GlobalEventListener } from "../global-event-listener/global-event-listener";
Expand Down Expand Up @@ -74,14 +74,10 @@ function toggle(set: Set<string>, value: string): Set<string> {
return set.has(value) ? set.remove(value) : set.add(value);
}

interface QueryProps {
essence: Essence;
timekeeper: Timekeeper;
dimension: Dimension;
searchText: string;
}

export class SelectableStringFilterMenu extends React.Component<SelectableStringFilterMenuProps, SelectableStringFilterMenuState> {
static contextType = ApiContext;

context: ApiContextValue;
private lastSearchText: string;

state: SelectableStringFilterMenuState = {
Expand Down Expand Up @@ -109,14 +105,14 @@ export class SelectableStringFilterMenu extends React.Component<SelectableString
private sendQueryFilter(): Promise<DatasetRequest> {
const { searchText } = this.state;
this.lastSearchText = searchText;
return this.debouncedQueryFilter({ ...this.props, searchText });
return this.debouncedQueryFilter(this.props.essence, this.constructSearchTextClause());
}

private queryFilter = (props: QueryProps): Promise<DatasetRequest> => {
const { essence, searchText } = props;
const query = stringFilterOptionsQuery({ ...props, limit: TOP_N + 1 });
private queryFilter = (essence: Essence, clause: StringFilterClause): Promise<DatasetRequest> => {
const { searchText } = this.state;
const { stringFilterQuery } = this.context;

return essence.dataCube.executor(query, { timezone: essence.timezone })
return stringFilterQuery(essence, clause)
.then((dataset: Dataset) => {
if (this.lastSearchText !== searchText) return null;
return loaded(dataset);
Expand Down Expand Up @@ -163,7 +159,7 @@ export class SelectableStringFilterMenu extends React.Component<SelectableString

updateSearchText = (searchText: string) => this.setState({ searchText });

constructClause(): FilterClause | null {
constructClause(): StringFilterClause {
const { dimension, filterMode } = this.props;
const { selectedValues } = this.state;
const { name } = dimension;
Expand All @@ -177,6 +173,17 @@ export class SelectableStringFilterMenu extends React.Component<SelectableString
});
}

constructSearchTextClause(): StringFilterClause {
const { dimension } = this.props;
const { name: reference } = dimension;
const { searchText } = this.state;
return new StringFilterClause({
action: StringFilterAction.CONTAINS,
reference,
values: Set.of(searchText)
});
}

onValueClick = (value: string, withModKey: boolean) => {
const { selectedValues } = this.state;
if (withModKey) {
Expand Down
23 changes: 18 additions & 5 deletions src/client/views/cube-view/api-context.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,10 @@ import React, { useContext } from "react";
import { ClientAppSettings } from "../../../common/models/app-settings/app-settings";
import { Dimension } from "../../../common/models/dimension/dimension";
import { Essence } from "../../../common/models/essence/essence";
import { StringFilterClause } from "../../../common/models/filter-clause/filter-clause";
import { Binary, Nullary, Unary } from "../../../common/utils/functional/functional";
import { DEFAULT_VIEW_DEFINITION_VERSION, definitionConverters } from "../../../common/view-definitions";
import { filterDefinitionConverter } from "../../../common/view-definitions/version-4/filter-definition";
import { Ajax } from "../../utils/ajax/ajax";

export type VisualizationQuery = Unary<Essence, Promise<Dataset>>;
Expand All @@ -29,10 +31,12 @@ export type RawDataQuery = Unary<Essence, Promise<Dataset>>;

export type BooleanFilterQuery = Binary<Essence, Dimension, Promise<Dataset>>;

export type StringFilterQuery = Binary<Essence, StringFilterClause, Promise<Dataset>>;
export interface ApiContextValue {
visualizationQuery: VisualizationQuery;
booleanFilterQuery: BooleanFilterQuery;
rawDataQuery: RawDataQuery;
stringFilterQuery: StringFilterQuery;
}

export const ApiContext = React.createContext<ApiContextValue>({
Expand All @@ -44,6 +48,9 @@ export const ApiContext = React.createContext<ApiContextValue>({
},
get rawDataQuery(): RawDataQuery {
throw new Error("Attempted to consume ApiContext when there was no Provider");
},
get stringFilterQuery(): StringFilterQuery {
throw new Error("Attempted to consume ApiContext when there was no Provider");
}
});

Expand All @@ -55,7 +62,7 @@ interface QueryResponse {
result: DatasetJS;
}

type QueryEndpoints = "visualization" | "boolean-filter" | "raw-data";
type QueryEndpoints = "visualization" | "boolean-filter" | "string-filter" | "number-filter" | "raw-data";

type ExtraParams = Record<string, unknown>;

Expand All @@ -66,8 +73,7 @@ const emptyParams: Nullary<ExtraParams> = () => ({});

function createApiCall<T extends SerializeExtraBase>(settings: ClientAppSettings, query: QueryEndpoints, serializeExtraParams: T): QueryFunction<T> {
const { oauth, clientTimeout: timeout } = settings;
const viewDefinitionVersion = DEFAULT_VIEW_DEFINITION_VERSION;
const converter = definitionConverters[viewDefinitionVersion];
const converter = definitionConverters[DEFAULT_VIEW_DEFINITION_VERSION];
return (essence: Essence, ...args: Parameters<T>) => {
const extra = serializeExtraParams(...args);
const { dataCube: { name } } = essence;
Expand All @@ -77,7 +83,6 @@ function createApiCall<T extends SerializeExtraBase>(settings: ClientAppSettings
url: `query/${query}`,
timeout,
data: {
viewDefinitionVersion,
dataCube: name,
viewDefinition,
...extra
Expand All @@ -96,6 +101,13 @@ function createBooleanFilterQuery(settings: ClientAppSettings): BooleanFilterQue
return createApiCall(settings, "boolean-filter", (dimension: Dimension) => ({ dimension: dimension.name }));
}

function createStringFilterQuery(settings: ClientAppSettings): StringFilterQuery {
return createApiCall(settings, "string-filter", (clause: StringFilterClause) => {
const clauseJS = filterDefinitionConverter.fromFilterClause(clause);
return ({ clause: clauseJS });
});
}

function createRawDataQueryApi(settings: ClientAppSettings): RawDataQuery {
return createApiCall(settings, "raw-data", emptyParams);
}
Expand All @@ -104,7 +116,8 @@ function createApi(settings: ClientAppSettings): ApiContextValue {
return {
booleanFilterQuery: createBooleanFilterQuery(settings),
visualizationQuery: createVizQueryApi(settings),
rawDataQuery: createRawDataQueryApi(settings)
rawDataQuery: createRawDataQueryApi(settings),
stringFilterQuery: createStringFilterQuery(settings)
};
}

Expand Down
61 changes: 0 additions & 61 deletions src/common/utils/query/preview-string-filter-query.ts

This file was deleted.

47 changes: 0 additions & 47 deletions src/common/utils/query/selectable-string-filter-query.ts

This file was deleted.

6 changes: 3 additions & 3 deletions src/common/view-definitions/version-4/filter-definition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

import { Duration } from "chronoshift";
import { List, Set } from "immutable";
import { ClientDataCube } from "../../models/data-cube/data-cube";
import { DataCube } from "../../models/data-cube/data-cube";
import { DateRange } from "../../models/date-range/date-range";
import { Dimension } from "../../models/dimension/dimension";
import { findDimensionByName } from "../../models/dimension/dimensions";
Expand Down Expand Up @@ -218,13 +218,13 @@ const filterClauseConverters: { [type in FilterType]: FilterDefinitionConversion
};

export interface FilterDefinitionConverter {
toFilterClause(filter: FilterClauseDefinition, dataCube: ClientDataCube): FilterClause;
toFilterClause(filter: FilterClauseDefinition, dataCube: Pick<DataCube, "dimensions" | "name">): FilterClause;

fromFilterClause(filterClause: FilterClause): FilterClauseDefinition;
}

export const filterDefinitionConverter: FilterDefinitionConverter = {
toFilterClause(clauseDefinition: FilterClauseDefinition, dataCube: ClientDataCube): FilterClause {
toFilterClause(clauseDefinition: FilterClauseDefinition, dataCube: Pick<DataCube, "dimensions" | "name">): FilterClause {
if (clauseDefinition.ref == null) {
throw new Error("Dimension name cannot be empty.");
}
Expand Down

0 comments on commit 4df5eb4

Please sign in to comment.