import {SignalRef} from 'vega-typings/types';import {isArray} from 'vega-util';import {FieldName, valueExpr, vgField} from './channeldef';import {DateTime} from './datetime';import {ExprRef} from './expr';import {LogicalComposition} from './logical';import {fieldExpr as timeUnitFieldExpr, normalizeTimeUnit, TimeUnit, TimeUnitParams} from './timeunit';import {isSignalRef} from './vega.schema';
export type Predicate = | FieldEqualPredicate | FieldRangePredicate | FieldOneOfPredicate | FieldLTPredicate | FieldGTPredicate | FieldLTEPredicate | FieldGTEPredicate | FieldValidPredicate | SelectionPredicate | string;
export type FieldPredicate = | FieldEqualPredicate | FieldLTPredicate | FieldGTPredicate | FieldLTEPredicate | FieldGTEPredicate | FieldRangePredicate | FieldOneOfPredicate | FieldValidPredicate;
export interface SelectionPredicate { selection: LogicalComposition<string>;}
export function isSelectionPredicate(predicate: LogicalComposition<Predicate>): predicate is SelectionPredicate { return predicate?.['selection'];}
export interface FieldPredicateBase {
timeUnit?: TimeUnit | TimeUnitParams;
field: FieldName;}
export interface FieldEqualPredicate extends FieldPredicateBase { equal: string | number | boolean | DateTime | ExprRef | SignalRef;}
export function isFieldEqualPredicate(predicate: any): predicate is FieldEqualPredicate { return predicate && !!predicate.field && predicate.equal !== undefined;}
export interface FieldLTPredicate extends FieldPredicateBase { lt: string | number | DateTime | ExprRef | SignalRef;}
export function isFieldLTPredicate(predicate: any): predicate is FieldLTPredicate { return predicate && !!predicate.field && predicate.lt !== undefined;}
export interface FieldLTEPredicate extends FieldPredicateBase { lte: string | number | DateTime | ExprRef | SignalRef;}
export function isFieldLTEPredicate(predicate: any): predicate is FieldLTEPredicate { return predicate && !!predicate.field && predicate.lte !== undefined;}
export interface FieldGTPredicate extends FieldPredicateBase { gt: string | number | DateTime | ExprRef | SignalRef;}
export function isFieldGTPredicate(predicate: any): predicate is FieldGTPredicate { return predicate && !!predicate.field && predicate.gt !== undefined;}
export interface FieldGTEPredicate extends FieldPredicateBase { gte: string | number | DateTime | ExprRef | SignalRef;}
export function isFieldGTEPredicate(predicate: any): predicate is FieldGTEPredicate { return predicate && !!predicate.field && predicate.gte !== undefined;}
export interface FieldRangePredicate extends FieldPredicateBase { range: (number | DateTime | null | ExprRef | SignalRef)[] | ExprRef | SignalRef;}
export function isFieldRangePredicate(predicate: any): predicate is FieldRangePredicate { if (predicate && predicate.field) { if (isArray(predicate.range) && predicate.range.length === 2) { return true; } else if (isSignalRef(predicate.range)) { return true; } } return false;}
export interface FieldOneOfPredicate extends FieldPredicateBase { oneOf: string[] | number[] | boolean[] | DateTime[];}
export interface FieldValidPredicate extends FieldPredicateBase { valid: boolean;}
export function isFieldOneOfPredicate(predicate: any): predicate is FieldOneOfPredicate { return ( predicate && !!predicate.field && (isArray(predicate.oneOf) || isArray(predicate.in)) );}
export function isFieldValidPredicate(predicate: any): predicate is FieldValidPredicate { return predicate && !!predicate.field && predicate.valid !== undefined;}
export function isFieldPredicate( predicate: Predicate): predicate is | FieldOneOfPredicate | FieldEqualPredicate | FieldRangePredicate | FieldLTPredicate | FieldGTPredicate | FieldLTEPredicate | FieldGTEPredicate { return ( isFieldOneOfPredicate(predicate) || isFieldEqualPredicate(predicate) || isFieldRangePredicate(predicate) || isFieldLTPredicate(predicate) || isFieldGTPredicate(predicate) || isFieldLTEPredicate(predicate) || isFieldGTEPredicate(predicate) );}
function predicateValueExpr(v: number | string | boolean | DateTime | ExprRef | SignalRef, timeUnit: TimeUnit) { return valueExpr(v, {timeUnit, wrapTime: true});}
function predicateValuesExpr(vals: (number | string | boolean | DateTime)[], timeUnit: TimeUnit) { return vals.map(v => predicateValueExpr(v, timeUnit));}
export function fieldFilterExpression(predicate: FieldPredicate, useInRange = true) { const {field} = predicate; const timeUnit = normalizeTimeUnit(predicate.timeUnit)?.unit; const fieldExpr = timeUnit ? 'time(' + timeUnitFieldExpr(timeUnit, field) + ')' : vgField(predicate, {expr: 'datum'});
if (isFieldEqualPredicate(predicate)) { return fieldExpr + '===' + predicateValueExpr(predicate.equal, timeUnit); } else if (isFieldLTPredicate(predicate)) { const upper = predicate.lt; return `${fieldExpr}<${predicateValueExpr(upper, timeUnit)}`; } else if (isFieldGTPredicate(predicate)) { const lower = predicate.gt; return `${fieldExpr}>${predicateValueExpr(lower, timeUnit)}`; } else if (isFieldLTEPredicate(predicate)) { const upper = predicate.lte; return `${fieldExpr}<=${predicateValueExpr(upper, timeUnit)}`; } else if (isFieldGTEPredicate(predicate)) { const lower = predicate.gte; return `${fieldExpr}>=${predicateValueExpr(lower, timeUnit)}`; } else if (isFieldOneOfPredicate(predicate)) { return `indexof([${predicateValuesExpr(predicate.oneOf, timeUnit).join(',')}], ${fieldExpr}) !== -1`; } else if (isFieldValidPredicate(predicate)) { return fieldValidPredicate(fieldExpr, predicate.valid); } else if (isFieldRangePredicate(predicate)) { const {range} = predicate; const lower = isSignalRef(range) ? {signal: `${range.signal}[0]`} : range[0]; const upper = isSignalRef(range) ? {signal: `${range.signal}[1]`} : range[1];
if (lower !== null && upper !== null && useInRange) { return ( 'inrange(' + fieldExpr + ', [' + predicateValueExpr(lower, timeUnit) + ', ' + predicateValueExpr(upper, timeUnit) + '])' ); }
const exprs = []; if (lower !== null) { exprs.push(`${fieldExpr} >= ${predicateValueExpr(lower, timeUnit)}`); } if (upper !== null) { exprs.push(`${fieldExpr} <= ${predicateValueExpr(upper, timeUnit)}`); }
return exprs.length > 0 ? exprs.join(' && ') : 'true'; }
throw new Error(`Invalid field predicate: ${JSON.stringify(predicate)}`);}
export function fieldValidPredicate(fieldExpr: string, valid = true) { if (valid) { return `isValid(${fieldExpr}) && isFinite(+${fieldExpr})`; } else { return `!isValid(${fieldExpr}) || !isFinite(+${fieldExpr})`; }}
export function normalizePredicate(f: Predicate): Predicate { if (isFieldPredicate(f) && f.timeUnit) { return { ...f, timeUnit: normalizeTimeUnit(f.timeUnit)?.unit }; } return f;}