From 91325ec2904cb4d7937bfa77bff65db1ae4806b0 Mon Sep 17 00:00:00 2001 From: hustcc Date: Fri, 7 Aug 2020 15:39:27 +0800 Subject: [PATCH 1/6] feat: init version --- src/const.ts | 1 + src/query.ts | 90 ++++++++++++++++++++++++++++++++++++++++++---------- src/types.ts | 5 +++ 3 files changed, 79 insertions(+), 17 deletions(-) create mode 100644 src/const.ts diff --git a/src/const.ts b/src/const.ts new file mode 100644 index 0000000..7850ae7 --- /dev/null +++ b/src/const.ts @@ -0,0 +1 @@ +export const ID_COLUMN = '$$_id_$$'; \ No newline at end of file diff --git a/src/query.ts b/src/query.ts index e107598..b254066 100644 --- a/src/query.ts +++ b/src/query.ts @@ -1,11 +1,28 @@ -import { Data, Field } from './types'; +import * as _ from 'lodash'; +import { Data, Field, OrderBy, Datum } from './types'; +import { ID_COLUMN } from './const'; export class Query { - private data; + /** 原始数据 */ + private data: Data = []; + + private selectOption: Field[] = []; + private orderByOption: OrderBy = {}; + private groupByOption: string; + private limitOption: number = 10; constructor(data: Data) { - this.data = data; + this.data = this.generateUniqueId(data); + } + + /** + * 给每个数据设置唯一 id + */ + private generateUniqueId(data: Data) { + return _.map(data, (d: Datum) => { + return { ...d, [ID_COLUMN]: _.uniqueId() }; + }); } /** @@ -13,8 +30,7 @@ export class Query { * @param fields */ public select(...fields: Field[]): Query { - - // TODO + this.selectOption = fields; return this; } @@ -24,9 +40,11 @@ export class Query { * @param field * @param asc */ - public orderBy(fields: string, asc?: boolean): Query { - - // TODO + public orderBy(field: string, asc?: boolean): Query { + this.orderByOption = { + field, + asc, + }; return this; } @@ -34,12 +52,11 @@ export class Query { /** * 按照字段分组 * @param asc - * @param fields + * @param field */ - public groupBy(fields: string): Query { + public groupBy(field: string): Query { + this.groupByOption = field; - // TODO - return this; } @@ -48,8 +65,7 @@ export class Query { * @param n */ public limit(n: number): Query { - - // TODO + this.limitOption = n; return this; } @@ -58,9 +74,49 @@ export class Query { * 返回最后的查询数据 */ public record(): Data { - - // TODO + const { data, selectOption, groupByOption, orderByOption, limitOption } = this; - return []; + const r = _(data); + + // 1. 执行分组 + r.groupBy(groupByOption ? groupByOption : ID_COLUMN); + + // 2. 执行 select + const fields = _.map(selectOption, (f: Field) => f.field)); + r.mapValues((v: Data, k: string) => { + // 1. 执行所有的 fields + const aggr = _.filter(selectOption, (f: Field) => f.aggregate !== 'raw'); + const noneAggr = _.filter(selectOption, (f: Field) => f.aggregate === 'raw'); + + let record; + // 有聚合 + if (aggr.length) { + // 按照 max 字段取去最大 + record = _.maxBy(v, _.find(aggr, (f: Field) => f.aggregate === 'max').field); + record = record ? [record] : []; + + } else { + // 无聚合 + record = v; + } + + // 只取这些字段 + return _.map(record, (d: Datum) => _.pick(d, fields)); + }); + + // 打平 + r.flatten(); + + // 3. 执行 order by + if (orderByOption) { + r.sortBy((d: Datum) => d[orderByOption.field]); // 升降序 + } + + // 4. 执行 limit + if (limitOption) { + r.slice(0, limitOption); + } + + return r.values(); } } \ No newline at end of file diff --git a/src/types.ts b/src/types.ts index 0ca33a7..8a3f85f 100644 --- a/src/types.ts +++ b/src/types.ts @@ -12,4 +12,9 @@ export type Field = { readonly aggregate: 'sum' | 'max' | 'max' | 'raw'; // 可扩展 /** 字段名 */ readonly field: string; +} + +export type OrderBy = { + readonly field?: string; + readonly asc?: boolean; } \ No newline at end of file From a1ec227be28fab9409e53500099094757a4eca48 Mon Sep 17 00:00:00 2001 From: hustcc Date: Fri, 7 Aug 2020 16:40:17 +0800 Subject: [PATCH 2/6] test: add eslint + jest --- .eslintignore | 4 ++++ .eslintrc | 34 ++++++++++++++++++++++++++++++++++ .prettierrc | 8 ++++++++ __tests__/index-spec.ts | 10 ++++++++++ package.json | 26 +++++++++++++++++++++++++- src/const.ts | 2 +- src/data-set.ts | 3 +-- src/field.ts | 14 +++++++------- src/query.ts | 28 +++++++++++++--------------- src/types.ts | 6 +++--- tsconfig.json | 18 ++++++++++++++++++ 11 files changed, 124 insertions(+), 29 deletions(-) create mode 100644 .eslintignore create mode 100644 .eslintrc create mode 100644 .prettierrc create mode 100644 __tests__/index-spec.ts create mode 100644 tsconfig.json diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 0000000..b2f1269 --- /dev/null +++ b/.eslintignore @@ -0,0 +1,4 @@ +node_modules +dist/ +test +build/ \ No newline at end of file diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 0000000..97bc72f --- /dev/null +++ b/.eslintrc @@ -0,0 +1,34 @@ +{ + "extends": [ + "eslint:recommended", + "plugin:@typescript-eslint/recommended", + "plugin:@typescript-eslint/eslint-recommended", + "prettier" + ], + "parser": "@typescript-eslint/parser", + "plugins": ["prettier", "@typescript-eslint"], + "parserOptions": { + "ecmaVersion": 6, + "sourceType": "module", + "impliedStrict": true + }, + "rules": { + "no-sparse-arrays": 0, + "no-self-assign": 0, + "no-unused-vars": 0, // @typescript-eslint/no-unused-vars + "no-inner-declarations": 0, + "prettier/prettier": 2, + "@typescript-eslint/no-unused-vars": 1, + "@typescript-eslint/no-non-null-assertion": 0, + "@typescript-eslint/no-explicit-any": 0, + "@typescript-eslint/no-use-before-define": [2, { "functions": false }], + "@typescript-eslint/ban-ts-ignore": 0, + "@typescript-eslint/interface-name-prefix": 0, + "@typescript-eslint/no-empty-interface": 0, + "@typescript-eslint/camelcase": 0, + "@typescript-eslint/no-inferrable-types": 0, + "@typescript-eslint/explicit-function-return-type": 0, + "@typescript-eslint/type-annotation-spacing": 0, + "@typescript-eslint/no-empty-function": 1 + } +} diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..8ce060c --- /dev/null +++ b/.prettierrc @@ -0,0 +1,8 @@ +{ + "semi": true, + "singleQuote": true, + "trailingComma": "es5", + "bracketSpacing": true, + "printWidth": 120, + "arrowParens": "always" +} \ No newline at end of file diff --git a/__tests__/index-spec.ts b/__tests__/index-spec.ts new file mode 100644 index 0000000..f19f29a --- /dev/null +++ b/__tests__/index-spec.ts @@ -0,0 +1,10 @@ +import { MAX } from '../src'; + +describe('data-set', () => { + it('#1', () => { + expect(MAX('f')).toEqual({ + aggregate: 'max', + field: 'f', + }); + }); +}); \ No newline at end of file diff --git a/package.json b/package.json index 8ae1598..df799ed 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,31 @@ "description": "", "main": "index.js", "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" + "test": "jest", + "lint": "eslint --ext .ts ./src", + "fix": "eslint --ext .ts ./src -c .eslintrc --fix && prettier --write ./src" + }, + "devDependencies": { + "@types/jest": "^26.0.9", + "@typescript-eslint/eslint-plugin": "^2.0.0", + "@typescript-eslint/parser": "^2.34.0", + "eslint": "^6.1.0", + "eslint-config-prettier": "^6.0.0", + "eslint-plugin-prettier": "^3.1.0", + "jest": "^26.2.2", + "prettier": "^2.0.1", + "ts-jest": "^26.1.4", + "typescript": "^3.5.3" + }, + "jest": { + "preset": "ts-jest", + "collectCoverage": false, + "collectCoverageFrom": [ + "src/**/*.ts", + "!**/node_modules/**", + "!**/vendor/**" + ], + "testRegex": "/__tests__/.*-spec\\.ts?$" }, "repository": { "type": "git", diff --git a/src/const.ts b/src/const.ts index 7850ae7..448b798 100644 --- a/src/const.ts +++ b/src/const.ts @@ -1 +1 @@ -export const ID_COLUMN = '$$_id_$$'; \ No newline at end of file +export const ID_COLUMN = '$$_id_$$'; diff --git a/src/data-set.ts b/src/data-set.ts index 7e8b58f..9c23a6a 100644 --- a/src/data-set.ts +++ b/src/data-set.ts @@ -4,7 +4,6 @@ import { Query } from './query'; /** 前端数据集模块 */ export class DataSet { - private data = []; constructor(data: Data) { @@ -31,4 +30,4 @@ export class DataSet { public query() { return new Query(this.data); } -} \ No newline at end of file +} diff --git a/src/field.ts b/src/field.ts index c6ab0d2..31ec4d3 100644 --- a/src/field.ts +++ b/src/field.ts @@ -2,7 +2,7 @@ import { Field } from './types'; /** * 聚合求和 - * @param field + * @param field */ export function SUM(field: string): Field { return { @@ -13,33 +13,33 @@ export function SUM(field: string): Field { /** * 聚合 MAX - * @param field + * @param field */ export function MAX(field: string): Field { return { - aggregate: 'sum', + aggregate: 'max', field, }; } /** * 聚合 MIN - * @param field + * @param field */ export function MIN(field: string): Field { return { - aggregate: 'sum', + aggregate: 'min', field, }; } /** * 无聚合字段 - * @param field + * @param field */ export function RAW(field: string): Field { return { aggregate: 'raw', field, }; -} \ No newline at end of file +} diff --git a/src/query.ts b/src/query.ts index b254066..57b22bd 100644 --- a/src/query.ts +++ b/src/query.ts @@ -3,7 +3,6 @@ import { Data, Field, OrderBy, Datum } from './types'; import { ID_COLUMN } from './const'; export class Query { - /** 原始数据 */ private data: Data = []; @@ -21,13 +20,13 @@ export class Query { */ private generateUniqueId(data: Data) { return _.map(data, (d: Datum) => { - return { ...d, [ID_COLUMN]: _.uniqueId() }; + return { ...d, [ID_COLUMN]: _.uniqueId() }; }); } /** * 选择字段 - * @param fields + * @param fields */ public select(...fields: Field[]): Query { this.selectOption = fields; @@ -37,21 +36,21 @@ export class Query { /** * 按照字段排序 - * @param field - * @param asc + * @param field + * @param asc */ public orderBy(field: string, asc?: boolean): Query { this.orderByOption = { field, asc, }; - + return this; } /** * 按照字段分组 - * @param asc + * @param asc * @param field */ public groupBy(field: string): Query { @@ -62,11 +61,11 @@ export class Query { /** * 取 n 条数据 - * @param n + * @param n */ public limit(n: number): Query { this.limitOption = n; - + return this; } @@ -75,14 +74,14 @@ export class Query { */ public record(): Data { const { data, selectOption, groupByOption, orderByOption, limitOption } = this; - + const r = _(data); // 1. 执行分组 r.groupBy(groupByOption ? groupByOption : ID_COLUMN); // 2. 执行 select - const fields = _.map(selectOption, (f: Field) => f.field)); + const fields = _.map(selectOption, (f: Field) => f.field); r.mapValues((v: Data, k: string) => { // 1. 执行所有的 fields const aggr = _.filter(selectOption, (f: Field) => f.aggregate !== 'raw'); @@ -94,10 +93,9 @@ export class Query { // 按照 max 字段取去最大 record = _.maxBy(v, _.find(aggr, (f: Field) => f.aggregate === 'max').field); record = record ? [record] : []; - } else { // 无聚合 - record = v; + record = v; } // 只取这些字段 @@ -109,7 +107,7 @@ export class Query { // 3. 执行 order by if (orderByOption) { - r.sortBy((d: Datum) => d[orderByOption.field]); // 升降序 + r.sortBy((d: Datum) => d[orderByOption.field]); // 升降序 } // 4. 执行 limit @@ -119,4 +117,4 @@ export class Query { return r.values(); } -} \ No newline at end of file +} diff --git a/src/types.ts b/src/types.ts index 8a3f85f..3efc73b 100644 --- a/src/types.ts +++ b/src/types.ts @@ -9,12 +9,12 @@ export type Data = Datum[]; */ export type Field = { /** 聚合函数 */ - readonly aggregate: 'sum' | 'max' | 'max' | 'raw'; // 可扩展 + readonly aggregate: 'sum' | 'max' | 'min' | 'max' | 'raw'; // 可扩展 /** 字段名 */ readonly field: string; -} +}; export type OrderBy = { readonly field?: string; readonly asc?: boolean; -} \ No newline at end of file +}; diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..63a06e9 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,18 @@ +{ + "compilerOptions": { + "module": "commonjs", + "sourceMap": true, + "inlineSources": true, + "target": "es5", + "outDir": "lib", + "declaration": true, + "importHelpers": true, + "moduleResolution": "node", + "allowSyntheticDefaultImports": true, + "esModuleInterop": true, + "resolveJsonModule": true, + "skipLibCheck": true, + "lib": ["esnext", "dom"] + }, + "include": ["src"] +} \ No newline at end of file From 1383bf6201cf8e9580cce47b9ae7faaa19156a40 Mon Sep 17 00:00:00 2001 From: hustcc Date: Fri, 7 Aug 2020 16:58:19 +0800 Subject: [PATCH 3/6] test: add husky for pre-commit --- .gitignore | 3 ++- package.json | 8 +++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index b512c09..62562b7 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ -node_modules \ No newline at end of file +coverage +node_modules diff --git a/package.json b/package.json index df799ed..2d2a2dd 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "eslint": "^6.1.0", "eslint-config-prettier": "^6.0.0", "eslint-plugin-prettier": "^3.1.0", + "husky": "^4.2.5", "jest": "^26.2.2", "prettier": "^2.0.1", "ts-jest": "^26.1.4", @@ -22,7 +23,7 @@ }, "jest": { "preset": "ts-jest", - "collectCoverage": false, + "collectCoverage": true, "collectCoverageFrom": [ "src/**/*.ts", "!**/node_modules/**", @@ -30,6 +31,11 @@ ], "testRegex": "/__tests__/.*-spec\\.ts?$" }, + "husky": { + "hooks": { + "pre-commit": "npm run lint && npm run test" + } + }, "repository": { "type": "git", "url": "git+https://github.com/ProtoTeam/intern-repo.git" From 49f0c1a0da7f481afa3f5e587b7d56dec8976e07 Mon Sep 17 00:00:00 2001 From: hustcc Date: Fri, 7 Aug 2020 17:02:47 +0800 Subject: [PATCH 4/6] test: add github action --- __tests__/.github/workflows/ci.yml | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 __tests__/.github/workflows/ci.yml diff --git a/__tests__/.github/workflows/ci.yml b/__tests__/.github/workflows/ci.yml new file mode 100644 index 0000000..eec37de --- /dev/null +++ b/__tests__/.github/workflows/ci.yml @@ -0,0 +1,22 @@ +name: Build +# This workflow is triggered on pushes to the repository. +on: [push] + +jobs: + build: + # Job name is Greeting + name: Build + # This job runs on Linux + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Use Node.js 12 + uses: actions/setup-node@v1 + with: + node-version: 12.x + - name: install + run: npm i + - name: lint + run: npm run lint + - name: test + run: npm run test \ No newline at end of file From 631f0d343e68a2b38cc1c60dddf481b59ef69c63 Mon Sep 17 00:00:00 2001 From: hustcc Date: Fri, 7 Aug 2020 17:08:35 +0800 Subject: [PATCH 5/6] test: add github action --- {__tests__/.github => .github}/workflows/ci.yml | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {__tests__/.github => .github}/workflows/ci.yml (100%) diff --git a/__tests__/.github/workflows/ci.yml b/.github/workflows/ci.yml similarity index 100% rename from __tests__/.github/workflows/ci.yml rename to .github/workflows/ci.yml From 7ae53d8d21960de13aa3f2712754d128d6493825 Mon Sep 17 00:00:00 2001 From: hustcc Date: Fri, 7 Aug 2020 17:19:47 +0800 Subject: [PATCH 6/6] chore: add some code --- src/const.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/const.ts b/src/const.ts index 448b798..8e828cf 100644 --- a/src/const.ts +++ b/src/const.ts @@ -1 +1,3 @@ export const ID_COLUMN = '$$_id_$$'; + +export const X = 'x';