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

Feature :: Sort step form #228

Merged
merged 13 commits into from
Jul 12, 2019
Merged
45 changes: 4 additions & 41 deletions doc/steps.md
Original file line number Diff line number Diff line change
Expand Up @@ -734,52 +734,16 @@ the `select` is used, it will only keep selected columns in the output.
### `sort` step

Sort values in one or several columns. Order can be either 'asc' or 'desc'.
When sorting on several columns, order of columns specified in `columns` matters,
and the `order` parameter must be of same length as `columns`. By default, if
`order` is not specified, it is considered as 'asc'.
When sorting on several columns, order of columns specified in `columns` matters.

```javascript
{
name: 'sort',
columns: ['foo', 'bar'],
order: ['asc', 'desc']
columns: [{column: 'foo', order: 'asc'}, {column: 'bar', order: 'desc'}],
}
```

#### Example 1: sort on one column with default ordering

**Input dataset:**

| Label | Group | Value |
| ------- | ------- | ----- |
| Label 1 | Group 1 | 13 |
| Label 2 | Group 1 | 7 |
| Label 3 | Group 1 | 20 |
| Label 4 | Group 2 | 1 |
| Label 5 | Group 2 | 10 |
| Label 6 | Group 2 | 5 |

**Step configuration:**

```javascript
{
name: 'sort',
columns: ['Value'],
}
```

**Output dataset:**

| Company | Group | Value |
| ------- | ------- | ----- |
| Label 4 | Group 2 | 1 |
| Label 6 | Group 2 | 5 |
| Label 2 | Group 1 | 7 |
| Label 5 | Group 2 | 10 |
| Label 1 | Group 1 | 13 |
| Label 3 | Group 1 | 20 |

#### Example 2: sort on one column with default ordering
#### Example

**Input dataset:**

Expand All @@ -797,8 +761,7 @@ and the `order` parameter must be of same length as `columns`. By default, if
```javascript
{
name: 'sort',
columns: ['Group', 'Value'],
order: ['asc', 'desc'],
columns: [{ column: 'Group', order: 'asc'}, {column: 'Value', order: 'desc' }]
}
```

Expand Down
1 change: 1 addition & 0 deletions src/components/ActionMenu.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
<div class="action-menu__option" @click="createDeleteColumnStep">Delete column</div>
<div class="action-menu__option" @click="createStep('fillna')">Fill null values</div>
<div class="action-menu__option" @click="createStep('filter')">Filter values</div>
<div class="action-menu__option" @click="createStep('sort')">Sort values</div>
</div>
</div>
</popover>
Expand Down
63 changes: 63 additions & 0 deletions src/components/stepforms/SortStepForm.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
<template>
<div>
<step-form-title :title="title"></step-form-title>

<WidgetList
addFieldName="Add Column"
id="sortColumn"
v-model="sortColumns"
:defaultItem="defaultSortColumn"
:widget="widgetSortColumn"
:automatic-new-field="false"
></WidgetList>

<step-form-buttonbar :errors="errors" :cancel="cancelEdition" :submit="submit"></step-form-buttonbar>
</div>
</template>

<script lang="ts">
import { Prop } from 'vue-property-decorator';
import { SortStep } from '@/lib/steps';
import BaseStepForm from './StepForm.vue';
import { StepFormComponent } from '@/components/formlib';
import WidgetList from './WidgetList.vue';
import { SortColumnType } from '@/lib/steps';
import WidgetSortColumn from './WidgetSortColumn.vue';

@StepFormComponent({
vqbstep: 'sort',
name: 'sort-step-form',
components: {
WidgetList,
WidgetSortColumn,
},
})
export default class SortStepForm extends BaseStepForm<SortStep> {
@Prop({ type: Object, default: () => ({ name: 'sort', columns: [] }) })
initialStepValue!: SortStep;

readonly title: string = 'Sort';
widgetSortColumn = WidgetSortColumn;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Side note for later: I wonder if we could not register the widget globally and then use widget="widget-sort-column" in the template.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Issue for this => #240


get defaultSortColumn() {
const column = this.selectedColumns.length === 0 ? '' : this.selectedColumns[0];
const sortColumn: SortColumnType = {
column,
order: 'asc',
};
return sortColumn;
}

get sortColumns() {
if (this.editedStep.columns.length) {
return this.editedStep.columns;
} else {
return [this.defaultSortColumn];
}
}

set sortColumns(newval) {
this.editedStep.columns = [...newval];
}
}
</script>
2 changes: 1 addition & 1 deletion src/components/stepforms/WidgetList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -155,4 +155,4 @@ export default class WidgetList extends Vue {
top: calc(50% - 16px);
cursor: pointer;
}
</style>
</style>
82 changes: 82 additions & 0 deletions src/components/stepforms/WidgetSortColumn.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
<template>
<fieldset class="widget-sort__container">
<WidgetAutocomplete
id="columnInput"
:options="columnNames"
v-model="sort.column"
name="Column"
placeholder="Enter a column"
@input="setSelectedColumns({ column: sort.column })"
></WidgetAutocomplete>
<WidgetAutocomplete
id="sortOrderInput"
v-model="sort.order"
name="Order"
:options="['asc', 'desc']"
placeholder="Order by"
></WidgetAutocomplete>
</fieldset>
</template>
<script lang="ts">
import _ from 'lodash';
import { Component, Prop, Vue, Watch } from 'vue-property-decorator';
import { Getter, Mutation } from 'vuex-class';
import { MutationCallbacks } from '@/store/mutations';
import WidgetAutocomplete from './WidgetAutocomplete.vue';
import { SortColumnType } from '@/lib/steps';

const defaultValues: SortColumnType = {
column: '',
order: 'asc',
};

@Component({
name: 'widget-sort-column',
components: {
WidgetAutocomplete,
},
})
export default class WidgetSortColumn extends Vue {
@Prop({
type: Object,
default: () => ({
column: '',
order: 'asc',
}),
})
value!: SortColumnType;

@Getter columnNames!: string[];

@Mutation setSelectedColumns!: MutationCallbacks['setSelectedColumns'];

sort: SortColumnType = { ...this.value };

@Watch('value', { immediate: true, deep: true })
onSortChanged(newval: SortColumnType, oldval: SortColumnType) {
if (!_.isEqual(newval, oldval)) {
this.sort = { ...newval };
this.$emit('input', this.sort);
}
}
}
</script>
<style lang="scss" scoped>
@import '../../styles/_variables';
.widget-sort__container {
@extend %form-widget__container;
margin-bottom: 0;
padding-top: 12px;
padding-bottom: 4px;
}

.widget-autocomplete__container {
zegonz marked this conversation as resolved.
Show resolved Hide resolved
align-items: center;
flex-direction: row;
margin-bottom: 8px;
}
.widget-sort__container .widget-autocomplete__label {
margin-bottom: 0px;
width: 40%;
}
</style>
1 change: 1 addition & 0 deletions src/components/stepforms/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ import './PercentageStepForm.vue';
import './PivotStepForm.vue';
import './RenameStepForm.vue';
import './UnpivotStepForm.vue';
import './SortStepForm.vue';
2 changes: 2 additions & 0 deletions src/components/stepforms/schemas/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import percentageBuildSchema from './percentage';
import pivotSchema from './pivot';
import renameBuildSchema from './rename';
import unpivotSchema from './unpivot';
import sortSchema from './sort';

type buildSchemaType = ((form: any) => object) | object;

Expand All @@ -26,6 +27,7 @@ const factories: { [stepname: string]: buildSchemaType } = {
pivot: pivotSchema,
rename: renameBuildSchema,
unpivot: unpivotSchema,
sort: sortSchema,
};

/**
Expand Down
37 changes: 37 additions & 0 deletions src/components/stepforms/schemas/sort.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
export default {
$schema: 'http://json-schema.org/draft-07/schema#',
title: 'Sort column step',
type: 'object',
properties: {
name: {
type: 'string',
enum: ['sort'],
},
columns: {
type: 'array',
minItems: 1,
title: 'Columns to sort',
description: 'Columns to sort',
attrs: {
placeholder: 'Enter columns',
},
items: {
type: 'object',
title: 'Column to sort',
description: 'Column to sort',
properties: {
column: {
type: 'string',
minLength: 1,
},
order: {
type: 'string',
enum: ['asc', 'desc'],
},
},
},
},
},
required: ['name', 'columns'],
additionalProperties: false,
};
8 changes: 6 additions & 2 deletions src/lib/steps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,10 +106,14 @@ export type SelectStep = {
columns: string[];
};

export type SortColumnType = {
column: string;
order: 'asc' | 'desc';
};

export type SortStep = {
name: 'sort';
columns: string[];
order?: ('asc' | 'desc')[];
columns: SortColumnType[];
};

export type TopStep = {
Expand Down
6 changes: 2 additions & 4 deletions src/lib/translators/mongo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -248,10 +248,8 @@ function transformReplace(step: Readonly<ReplaceStep>): MongoStep {
/** transform a 'sort' step into corresponding mongo steps */
function transformSort(step: Readonly<SortStep>): MongoStep {
const sortMongo: PropMap<number> = {};
const sortOrders = step.order === undefined ? Array(step.columns.length).fill('asc') : step.order;
for (let i = 0; i < step.columns.length; i++) {
const order = sortOrders[i] === 'asc' ? 1 : -1;
sortMongo[step.columns[i]] = order;
for (const sortColumn of step.columns) {
sortMongo[sortColumn.column] = sortColumn.order === 'asc' ? 1 : -1;
}
return { $sort: sortMongo };
}
Expand Down
4 changes: 3 additions & 1 deletion tests/unit/karma-test-suite.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import './plugins.spec';
import './popover.spec';
import './rename-step-form.spec';
import './resizable-panels.spec';
import './sort-step-form.spec';
import './step.spec';
import './store.spec';
import './translator.spec';
Expand All @@ -31,4 +32,5 @@ import './widget-autocomplete.spec';
import './widget-checkbox.spec';
import './widget-input-text.spec';
import './widget-list.spec';
import './widget-multiselect.spec.ts';
import './widget-multiselect.spec';
import './widget-sort-column.spec';
24 changes: 2 additions & 22 deletions tests/unit/mongo.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -471,8 +471,7 @@ describe('Pipeline to mongo translator', () => {
const pipeline: Pipeline = [
{
name: 'sort',
columns: ['foo'],
order: ['desc'],
columns: [{ column: 'foo', order: 'desc' }],
},
];
const querySteps = mongo36translator.translate(pipeline);
Expand All @@ -489,8 +488,7 @@ describe('Pipeline to mongo translator', () => {
const pipeline: Pipeline = [
{
name: 'sort',
columns: ['foo', 'bar'],
order: ['asc', 'desc'],
columns: [{ column: 'foo', order: 'asc' }, { column: 'bar', order: 'desc' }],
},
];
const querySteps = mongo36translator.translate(pipeline);
Expand All @@ -504,24 +502,6 @@ describe('Pipeline to mongo translator', () => {
]);
});

it('can generate a sort step on multiple columns with default order', () => {
const pipeline: Pipeline = [
{
name: 'sort',
columns: ['foo', 'bar'],
},
];
const querySteps = mongo36translator.translate(pipeline);
expect(querySteps).to.eql([
{
$sort: {
foo: 1,
bar: 1,
},
},
]);
});

it('can generate a fillna step', () => {
const pipeline: Pipeline = [
{
Expand Down
Loading