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

number filter query #1029

Merged
merged 7 commits into from
Feb 13, 2023
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

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,14 @@
* limitations under the License.
*/

import { $, Dataset, ply } from "plywood";
import { Dataset } from "plywood";
import React from "react";
import { Dimension } from "../../../common/models/dimension/dimension";
import { Essence } from "../../../common/models/essence/essence";
import { Timekeeper } from "../../../common/models/timekeeper/timekeeper";
import { getNumberOfWholeDigits, isTruthy, toSignificantDigits } from "../../../common/utils/general/general";
import { clamp, classNames, getXFromEvent } from "../../utils/dom/dom";
import { ApiContext, ApiContextValue } from "../../views/cube-view/api-context";
import { Loader } from "../loader/loader";
import { QueryError } from "../query-error/query-error";
import { RangeHandle } from "../range-handle/range-handle";
Expand Down Expand Up @@ -72,6 +73,9 @@ export interface NumberRangePickerState {
}

export class NumberRangePicker extends React.Component<NumberRangePickerProps, NumberRangePickerState> {
static contextType = ApiContext;

context: ApiContextValue;
public mounted: boolean;
private picker = React.createRef<HTMLDivElement>();

Expand All @@ -87,19 +91,12 @@ export class NumberRangePicker extends React.Component<NumberRangePickerProps, N
}

fetchData(essence: Essence, timekeeper: Timekeeper, dimension: Dimension, rightBound: number): void {
const { dataCube } = essence;
const filterExpression = essence.getEffectiveFilter(timekeeper, { unfilterDimension: dimension }).toExpression(dataCube);
const $main = $("main");
const query = ply()
.apply("main", $main.filter(filterExpression))
.apply("Min", $main.min($(dimension.name)))
.apply("Max", $main.max($(dimension.name)));

const { numberFilterQuery } = this.context;
this.setState({
loading: true
});

dataCube.executor(query)
numberFilterQuery(essence, dimension)
.then(
(dataset: Dataset) => {
if (!this.mounted) return;
Expand Down
14 changes: 13 additions & 1 deletion src/client/views/cube-view/api-context.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,21 @@ export type RawDataQuery = Unary<Essence, Promise<Dataset>>;
export type BooleanFilterQuery = Binary<Essence, Dimension, Promise<Dataset>>;

export type StringFilterQuery = Binary<Essence, StringFilterClause, Promise<Dataset>>;

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

export interface ApiContextValue {
visualizationQuery: VisualizationQuery;
booleanFilterQuery: BooleanFilterQuery;
rawDataQuery: RawDataQuery;
stringFilterQuery: StringFilterQuery;
numberFilterQuery: NumberFilterQuery;
}

export const ApiContext = React.createContext<ApiContextValue>({
get numberFilterQuery(): NumberFilterQuery {
throw new Error("Attempted to consume ApiContext when there was no Provider");
},
get booleanFilterQuery(): BooleanFilterQuery {
throw new Error("Attempted to consume ApiContext when there was no Provider");
},
Expand All @@ -62,7 +69,7 @@ interface QueryResponse {
result: DatasetJS;
}

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

type ExtraParams = Record<string, unknown>;

Expand Down Expand Up @@ -97,6 +104,10 @@ function createVizQueryApi(settings: ClientAppSettings): VisualizationQuery {
return createApiCall(settings, "visualization", emptyParams);
}

function createNumberFilterQuery(settings: ClientAppSettings): NumberFilterQuery {
return createApiCall(settings, "number-filter", (dimension: Dimension) => ({ dimension: dimension.name }));
}

function createBooleanFilterQuery(settings: ClientAppSettings): BooleanFilterQuery {
return createApiCall(settings, "boolean-filter", (dimension: Dimension) => ({ dimension: dimension.name }));
}
Expand All @@ -114,6 +125,7 @@ function createRawDataQueryApi(settings: ClientAppSettings): RawDataQuery {

function createApi(settings: ClientAppSettings): ApiContextValue {
return {
numberFilterQuery: createNumberFilterQuery(settings),
booleanFilterQuery: createBooleanFilterQuery(settings),
visualizationQuery: createVizQueryApi(settings),
rawDataQuery: createRawDataQueryApi(settings),
Expand Down
4 changes: 2 additions & 2 deletions src/common/utils/functional/functional.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,10 +119,10 @@ export function range(from: number, to: number): number[] {
}

// TODO: fix to use infer on arguments tuple https://stackoverflow.com/a/50014868/1089761
export function debounceWithPromise<T extends (...args: any[]) => any>(fn: T, ms: number): ((...args: any[]) => Promise<any>) & { cancel: Fn } {
export function debounceWithPromise<T extends (...args: any[]) => Promise<any>>(fn: T, ms: number): ((...args: Parameters<T>) => Promise<any>) & { cancel: Fn } {
let timeoutId: any;

const debouncedFn = (...args: any[]) => {
const debouncedFn = (...args: Parameters<T>) => {
let resolve: Function;
const promise = new Promise(pResolve => {
resolve = pResolve;
Expand Down
29 changes: 29 additions & 0 deletions src/server/routes/query/query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,36 @@ export function queryRouter(settings: Pick<SettingsManager, "logger" | "getSourc
const queryDecorator = getQueryDecorator(req, dataCube, settings);
const result = await executeQuery(dataCube, query, essence.timezone, queryDecorator);
res.json({ result });
} catch (error) {
handleRequestErrors(error, res, settings.logger);
}
});

router.post("/number-filter", async (req: Request, res: Response) => {

function getQuery(essence: Essence, dimension: Dimension, timekeeper: Timekeeper): Expression {
const { dataCube } = essence;
const filterExpression = essence
.getEffectiveFilter(timekeeper, { unfilterDimension: dimension })
.toExpression(dataCube);
const $main = $("main");
return ply()
.apply("main", $main.filter(filterExpression))
.apply("Min", $main.min($(dimension.name)))
.apply("Max", $main.max($(dimension.name)));
}

try {
const dataCube = await parseDataCube(req, settings);
const viewDefinition = parseViewDefinition(req);
const dimension = parseDimension(req, dataCube);

const essence = createEssence(viewDefinition, converter, dataCube, settings.appSettings);

const query = getQuery(essence, dimension, settings.getTimekeeper());
const queryDecorator = getQueryDecorator(req, dataCube, settings);
const result = await executeQuery(dataCube, query, essence.timezone, queryDecorator);
res.json({ result });
} catch (error) {
handleRequestErrors(error, res, settings.logger);
}
Expand Down