Skip to content

Commit

Permalink
filter action and reducer (#415)
Browse files Browse the repository at this point in the history
* add filter reducer

* misc style fix; change filters to be mandatory attribute; modified tests

* moved array to util.ts

* changed filters to be an array of RangeFilter/OneOfFilter

* misc fixes

* reorder

* Remove unnecessary colons

* added FilterModify

* make filter reducers more fine grained

* fixed import
  • Loading branch information
starry97 authored and kanitw committed Jul 18, 2017
1 parent 8dad829 commit e61bd3b
Show file tree
Hide file tree
Showing 16 changed files with 346 additions and 55 deletions.
35 changes: 35 additions & 0 deletions src/actions/filter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import {DateTime} from 'vega-lite/build/src/datetime';
import {OneOfFilter, RangeFilter} from 'vega-lite/build/src/filter';
import {ReduxAction} from './redux-action';

export type FilterAction = FilterAdd | FilterRemove | FilterModifyMinBound | FilterModifyMaxBound |
FilterModifyOneOf;

export const FILTER_ADD = 'FILTER_ADD';
export type FilterAdd = ReduxAction<typeof FILTER_ADD, {
filter: RangeFilter | OneOfFilter,
index: number
}>;

export const FILTER_REMOVE = 'FILTER_REMOVE';
export type FilterRemove = ReduxAction<typeof FILTER_REMOVE, {
index: number
}>;

export const FILTER_MODIFY_MIN_BOUND = 'FILTER_MODIFY_MIN_BOUND';
export type FilterModifyMinBound = ReduxAction<typeof FILTER_MODIFY_MIN_BOUND, {
minBound: number | DateTime,
index: number
}>;

export const FILTER_MODIFY_MAX_BOUND = 'FILTER_MODIFY_MAX_BOUND';
export type FilterModifyMaxBound = ReduxAction<typeof FILTER_MODIFY_MAX_BOUND, {
maxBound: number | DateTime,
index: number
}>;

export const FILTER_MODIFY_ONE_OF = 'FILTER_MODIFY_ONE_OF';
export type FilterModifyOneOf = ReduxAction<typeof FILTER_MODIFY_ONE_OF, {
oneOf: string[] | number[] | boolean[] | DateTime[],
index: number
}>;
15 changes: 11 additions & 4 deletions src/actions/index.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,25 @@
import {ConfigAction} from './config';
import {DatasetAction} from './dataset';
import {FilterAction} from './filter';
import {ResultAction} from './result';
import {ShelfAction} from './shelf';
import {ApplicationStateAction} from './state';
import {UndoableAction} from './undo-redo';


export * from './config';
export * from './dataset';
export * from './result';
export * from './filter';
export * from './redux-action';
export * from './result';
export * from './shelf';
export * from './undo-redo';
export * from './state';
export * from './undo-redo';

/**
* Union type of all actions in our application.
*/
export type Action = DatasetAction | ShelfAction | UndoableAction |
ResultAction | ConfigAction | ApplicationStateAction;
ResultAction | ConfigAction | ApplicationStateAction | FilterAction;

export type ActionType = Action['type'];

Expand All @@ -31,6 +32,12 @@ const ACTION_TYPE_INDEX: {[k in ActionType]: 1} = {
DATASET_URL_RECEIVE: 1,
DATASET_INLINE_RECEIVE: 1,

FILTER_ADD: 1,
FILTER_MODIFY_MAX_BOUND: 1,
FILTER_MODIFY_MIN_BOUND: 1,
FILTER_MODIFY_ONE_OF: 1,
FILTER_REMOVE: 1,

RESULT_RECEIVE: 1,
RESULT_REQUEST: 1,

Expand Down
12 changes: 7 additions & 5 deletions src/components/view-pane/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,26 +4,27 @@ import * as React from 'react';
import * as CSSModules from 'react-css-modules';
import {connect} from 'react-redux';
import {Data} from 'vega-lite/build/src/data';

import {OneOfFilter, RangeFilter} from 'vega-lite/build/src/filter';
import {ActionHandler, createDispatchHandler} from '../../actions/redux-action';
import {ShelfAction} from '../../actions/shelf';
import {State} from '../../models';
import {extractPlotObjects, PlotObject} from '../../models/plot';
import {hasWildcards} from '../../models/shelf/spec';
import {getData, getMainResult, getQuery} from '../../selectors';
import {getTransforms, hasWildcards} from '../../models/shelf/spec';
import {getData, getFilters, getMainResult, getQuery} from '../../selectors';
import {Plot} from '../plot';
import {PlotList} from '../plot-list';
import * as styles from './view-pane.scss';

export interface ViewPaneProps extends ActionHandler<ShelfAction> {
data: Data;
query: Query;
filters: Array<RangeFilter | OneOfFilter>;
mainResult: SpecQueryGroup<PlotObject>;
}

class ViewPaneBase extends React.PureComponent<ViewPaneProps, {}> {
public render() {
const {data, handleAction, query, mainResult} = this.props;
const {data, handleAction, filters, query, mainResult} = this.props;
const isSpecific = !hasWildcards(query.spec).hasAnyWildcard;

// if there are no results, then nothing to render.
Expand All @@ -35,6 +36,7 @@ class ViewPaneBase extends React.PureComponent<ViewPaneProps, {}> {
const spec = {
// FIXME: include data in the main spec?
data: data,
transform: getTransforms(filters),
...getTopSpecQueryItem(mainResult).spec
};

Expand Down Expand Up @@ -65,7 +67,7 @@ export const ViewPane = connect(
return {
data: getData(state),
query: getQuery(state),

filters: getFilters(state),
// FIXME: refactor the flow for this part (we should support asynchrounous request for this too)
mainResult: getMainResult(state)
};
Expand Down
5 changes: 4 additions & 1 deletion src/models/plot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,10 @@ export function extractPlotObjects(modelGroup: SpecQueryGroup<PlotObject>) {
});
}

export function convertToPlotObjectsGroup(modelGroup: SpecQueryModelGroup, data: Data): SpecQueryGroup<PlotObject> {
export function convertToPlotObjectsGroup(
modelGroup: SpecQueryModelGroup,
data: Data
): SpecQueryGroup<PlotObject> {
const items = modelGroup.items.map(item => {
if (isSpecQueryGroup<SpecQueryModel>(item)) {
const childModelGroup = item as SpecQueryModelGroup;
Expand Down
8 changes: 8 additions & 0 deletions src/models/shelf/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ describe('models/shelf', () => {
it('makes a query that has an additional fieldQuery with wildcard channel', () => {
expect(
autoAddFieldQuery({
filters: [],
mark: 'point',
encoding: {
x: {field: 'a', type: 'quantitative'}
Expand All @@ -14,6 +15,7 @@ describe('models/shelf', () => {
}, {field: 'b', type: 'nominal'})
).toEqual({
spec: {
transform: [],
mark: 'point',
encodings: [
{channel: 'x', field: 'a', type: 'quantitative'},
Expand All @@ -31,6 +33,7 @@ describe('models/shelf', () => {
'if there is no wildcard', () => {
expect(toQuery({
spec: {
filters: [],
mark: 'point',
encoding: {
x: {field: 'a', type: 'quantitative'}
Expand All @@ -41,6 +44,7 @@ describe('models/shelf', () => {
specPreview: null
})).toEqual({
spec: {
transform: [],
mark: 'point',
encodings: [
{channel: 'x', field: 'a', type: 'quantitative'},
Expand All @@ -59,6 +63,7 @@ describe('models/shelf', () => {
it('returns the query that groups by field and auto add count if there is a wildcard field', () => {
expect(toQuery({
spec: {
filters: [],
mark: 'point',
encoding: {
x: {field: '?', type: 'quantitative'}
Expand All @@ -69,6 +74,7 @@ describe('models/shelf', () => {
specPreview: null
})).toEqual({
spec: {
transform: [],
mark: 'point',
encodings: [
{channel: 'x', field: '?', type: 'quantitative'},
Expand All @@ -88,6 +94,7 @@ describe('models/shelf', () => {
'if there is a wildcard field and function', () => {
expect(toQuery({
spec: {
filters: [],
mark: 'point',
encoding: {
x: {aggregate: '?', field: '?', type: 'quantitative'}
Expand All @@ -98,6 +105,7 @@ describe('models/shelf', () => {
specPreview: null
})).toEqual({
spec: {
transform: [],
mark: 'point',
encodings: [
{channel: 'x', aggregate: '?', field: '?', type: 'quantitative'},
Expand Down
9 changes: 6 additions & 3 deletions src/models/shelf/spec.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ describe('models/shelf/unit', () => {
anyEncodings: [
{channel: SHORT_WILDCARD, field: 'b', type: 'ordinal'}
],
config: {numberFormat: 'd'}
config: {numberFormat: 'd'},
filters: []
});
});
});
Expand All @@ -37,14 +38,16 @@ describe('models/shelf/unit', () => {
anyEncodings: [
{channel: SHORT_WILDCARD, field: 'b', type: 'ordinal'}
],
config: {numberFormat: 'd'}
config: {numberFormat: 'd'},
filters: []
})).toEqual({
mark: 'point',
encodings: [
{channel: 'x', field: 'a', type: 'quantitative'},
{channel: '?', field: 'b', type: 'ordinal'}
],
config: {numberFormat: 'd'}
config: {numberFormat: 'd'},
transform: []
});
});
});
Expand Down
38 changes: 33 additions & 5 deletions src/models/shelf/spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,20 @@ import {EncodingQuery, isAutoCountQuery, isValueQuery} from 'compassql/build/src
import {SpecQuery} from 'compassql/build/src/query/spec';
import {isWildcard, isWildcardDef, SHORT_WILDCARD} from 'compassql/build/src/wildcard';
import {Config} from 'vega-lite/build/src/config';
import {isOneOfFilter, isRangeFilter, OneOfFilter, RangeFilter} from 'vega-lite/build/src/filter';
import {FilterTransform, isFilter, Transform} from 'vega-lite/build/src/transform';
import {fromEncodingQueries, ShelfAnyEncodingDef, ShelfMark, SpecificEncoding} from './encoding';


/**
* A model state for the shelf of a unit specification.
* This interface provides a hybrid structure that resembles
* FacetedCompositeUnitSpec in Vega-Lite and SpecQuery in CompassQL,
* but provide structure that better serves as internal structure of shelf in Voyager.
*/
export interface ShelfUnitSpec {
mark: ShelfMark;
filters: Array<RangeFilter | OneOfFilter>;

// TODO: add transform
mark: ShelfMark;

/**
* Mapping between specific encoding channels and encoding definitions.
Expand All @@ -32,20 +33,21 @@ export interface ShelfUnitSpec {

export function toSpecQuery(spec: ShelfUnitSpec): SpecQuery {
return {
transform: getTransforms(spec.filters),
mark: spec.mark,
encodings: specificEncodingsToEncodingQueries(spec.encoding).concat(spec.anyEncodings),
config: spec.config
};
}

export function fromSpecQuery(spec: SpecQuery, oldConfig?: Config): ShelfUnitSpec {
const {mark, encodings, config} = spec;

const {mark, encodings, config, transform} = spec;
if (isWildcardDef(mark)) {
throw new Error('Voyager 2 does not support custom wildcard mark yet');
}

return {
filters: getFilters(transform),
mark,
...fromEncodingQueries(encodings),
config: config || oldConfig
Expand Down Expand Up @@ -107,7 +109,33 @@ function specificEncodingsToEncodingQueries(encoding: SpecificEncoding): Encodin
return encodings;
}

export function getFilters(transforms: Transform[]): Array<RangeFilter|OneOfFilter> {
if (!transforms) {
return [];
} else {
const filters: Array<RangeFilter|OneOfFilter> = [];
transforms.map(transform => {
if (!isFilter(transform)) {
throw new Error('Voyager does not support transforms other than FilterTransform');
} else if (!isRangeFilter(transform.filter) && !isOneOfFilter(transform.filter)) {
throw new Error('Voyager does not support filters other than RangeFilter and OneOfFilter');
}
filters.push(transform.filter);
});
return filters;
}
}

export function getTransforms(filters: Array<RangeFilter|OneOfFilter>) {
const transform: FilterTransform[] = [];
filters.map(filter => {
transform.push({filter: filter});
});
return transform;
}

export const DEFAULT_SHELF_UNIT_SPEC: Readonly<ShelfUnitSpec> = {
filters: [],
mark: SHORT_WILDCARD,
encoding: {},
anyEncodings: [],
Expand Down
15 changes: 14 additions & 1 deletion src/reducers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,13 @@ import {
DATASET_SCHEMA_CHANGE_ORDINAL_DOMAIN,
DATASET_URL_RECEIVE,
DATASET_URL_REQUEST,
FILTER_ADD,

FILTER_MODIFY_MAX_BOUND,
FILTER_MODIFY_MIN_BOUND,
FILTER_MODIFY_ONE_OF,

FILTER_REMOVE,
RESULT_RECEIVE,
RESULT_REQUEST,
SET_APPLICATION_STATE,
Expand Down Expand Up @@ -80,6 +87,12 @@ export const USER_ACTIONS: ActionType[] = [
DATASET_SCHEMA_CHANGE_FIELD_TYPE,
DATASET_SCHEMA_CHANGE_ORDINAL_DOMAIN,
DATASET_URL_REQUEST,
// Filter Actions
FILTER_ADD,
FILTER_MODIFY_MAX_BOUND,
FILTER_MODIFY_MIN_BOUND,
FILTER_MODIFY_ONE_OF,
FILTER_REMOVE,
// Shelf Actions,
SHELF_CLEAR,
SHELF_MARK_CHANGE_TYPE,
Expand All @@ -90,7 +103,7 @@ export const USER_ACTIONS: ActionType[] = [
SHELF_FUNCTION_CHANGE,
SHELF_SPEC_LOAD,
SHELF_SPEC_PREVIEW,
SHELF_SPEC_PREVIEW_DISABLE,
SHELF_SPEC_PREVIEW_DISABLE
];


Expand Down
Loading

0 comments on commit e61bd3b

Please sign in to comment.