UNPKG

virool-pivot

Version:

A web-based exploratory visualization UI for Druid.io

172 lines (146 loc) 5.26 kB
import { Class, Instance, isInstanceOf } from 'immutable-class'; import { Timezone, Duration, minute } from 'chronoshift'; import { $, r, Expression, ExpressionJS, LiteralExpression, RefExpression, Set, SetJS, ChainExpression, NotAction, OverlapAction, InAction, Range, TimeRange, Datum } from 'plywood'; // Basically these represent // expression.in(selection) .not()? export interface FilterClauseValue { expression: Expression; selection?: Expression; exclude?: boolean; } export interface FilterClauseJS { expression: ExpressionJS; selection?: ExpressionJS; exclude?: boolean; } function isLiteral(ex: Expression): boolean { if (ex instanceof LiteralExpression) return TimeRange.isTimeRange(ex.value) || Set.isSet(ex.value); return false; } function isRelative(ex: Expression): boolean { if (ex instanceof ChainExpression) { if (ex.type !== 'TIME_RANGE') return false; var expression = ex.expression; if (expression instanceof RefExpression) { return expression.name === FilterClause.NOW_REF_NAME || expression.name === FilterClause.MAX_TIME_REF_NAME; } } return false; } var check: Class<FilterClauseValue, FilterClauseJS>; export class FilterClause implements Instance<FilterClauseValue, FilterClauseJS> { static isFilterClause(candidate: any): candidate is FilterClause { return isInstanceOf(candidate, FilterClause); } static NOW_REF_NAME = 'n'; static MAX_TIME_REF_NAME = 'm'; static evaluate(selection: Expression, now: Date, maxTime: Date, timezone: Timezone): TimeRange { if (!selection) return null; var maxTimeMinuteTop = minute.shift(minute.floor(maxTime, timezone), timezone, 1); var datum: Datum = {}; datum[FilterClause.NOW_REF_NAME] = now; datum[FilterClause.MAX_TIME_REF_NAME] = maxTimeMinuteTop; return selection.defineEnvironment({ timezone }).getFn()(datum, {}); } static fromExpression(ex: Expression): FilterClause { var exclude = false; if (ex.lastAction() instanceof NotAction) { ex = ex.popAction(); exclude = true; } var lastAction = ex.lastAction(); var dimExpression = ex.popAction(); if (lastAction instanceof InAction || lastAction instanceof OverlapAction) { var selection = lastAction.expression; return new FilterClause({ expression: dimExpression, selection, exclude }); } throw new Error(`invalid expression ${ex.toString()}`); } static fromJS(parameters: FilterClauseJS): FilterClause { var value: FilterClauseValue = { expression: Expression.fromJS(parameters.expression), selection: Expression.fromJS(parameters.selection), exclude: Boolean(parameters.exclude) }; return new FilterClause(value); } public expression: Expression; public selection: Expression; public exclude: boolean; public relative: boolean; constructor(parameters: FilterClauseValue) { this.expression = parameters.expression; var selection = parameters.selection; if (isRelative(selection)) { this.relative = true; } else if (isLiteral(selection)) { this.relative = false; } else { throw new Error(`invalid expression ${selection.toString()}`); } this.selection = selection; this.exclude = parameters.exclude || false; } public valueOf(): FilterClauseValue { return { expression: this.expression, selection: this.selection, exclude: this.exclude }; } public toJS(): FilterClauseJS { var js: FilterClauseJS = { expression: this.expression.toJS(), selection: this.selection.toJS() }; if (this.exclude) js.exclude = true; return js; } public toJSON(): FilterClauseJS { return this.toJS(); } public toString(): string { return `[FilterClause: ${this.expression.toString()}]`; } public equals(other: FilterClause): boolean { return FilterClause.isFilterClause(other) && this.expression.equals(other.expression) && this.selection.equals(other.selection) && this.exclude === other.exclude; } public toExpression(): ChainExpression { const { expression, selection } = this; var ex: ChainExpression = null; var selectionType = selection.type; if (selectionType === 'TIME_RANGE' || selectionType === 'SET/TIME_RANGE' || selectionType === 'NUMBER_RANGE' || selectionType === 'SET/NUMBER_RANGE') { ex = expression.in(selection); } else { ex = expression.overlap(selection); } if (this.exclude) ex = ex.not(); return ex; } public getLiteralSet(): Set { if (this.relative) return null; var v = this.selection.getLiteralValue(); return TimeRange.isTimeRange(v) ? Set.fromJS([v]) : v; } public getExtent(): Range<any> { var mySet = this.getLiteralSet(); return mySet ? mySet.extent() : null; } public changeSelection(selection: Expression) { var value = this.valueOf(); value.selection = selection; return new FilterClause(value); } public evaluate(now: Date, maxTime: Date, timezone: Timezone): FilterClause { if (!this.relative) return this; return this.changeSelection(r(FilterClause.evaluate(this.selection, now, maxTime, timezone))); } } check = FilterClause;