Skip to content

Commit 1f04805

Browse files
committed
feat: add lib query builder
1 parent f05ca6a commit 1f04805

5 files changed

Lines changed: 204 additions & 0 deletions

File tree

src/lib/query-builder/filtered.ts

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import { ObjectLiteral } from 'typeorm'
2+
import { validate } from '../validate'
3+
import { ApplyFilterParams, QueryFilters } from './types'
4+
5+
export function applyFilter<T extends ObjectLiteral>({
6+
query,
7+
filters,
8+
model,
9+
options,
10+
}: ApplyFilterParams<T>) {
11+
let filtered: QueryFilters[] = []
12+
13+
if (Array.isArray(filters)) {
14+
filtered = filters
15+
} else {
16+
filtered = JSON.parse(filters) as QueryFilters[]
17+
}
18+
19+
if (filtered.length > 0) {
20+
for (let i = 0; i < filtered.length; i += 1) {
21+
const item = filtered[i]
22+
23+
const check_uuid = validate.uuid(item.value)
24+
const check_numeric = validate.number(item.value)
25+
const check_boolean = validate.boolean(item.value)
26+
const expect_number_uuid_boolean = !check_numeric && !check_uuid && !check_boolean
27+
28+
const postgres_driver = options?.type === 'postgres'
29+
const mysql_driver = ['mysql', 'mariadb'].includes(String(options?.type))
30+
31+
if (check_uuid || check_numeric || check_boolean) {
32+
query.andWhere(`${model}.${item.id} = :${item.id}`, {
33+
[`${item.id}`]: `${item.value}`,
34+
})
35+
}
36+
37+
if (mysql_driver && expect_number_uuid_boolean) {
38+
query.andWhere(`${model}.${item.id} LIKE :${item.id}`, {
39+
[`${item.id}`]: `%${item.value}%`,
40+
})
41+
}
42+
43+
if (postgres_driver && expect_number_uuid_boolean) {
44+
query.andWhere(`${model}.${item.id} ILIKE :${item.id}`, {
45+
[`${item.id}`]: `%${item.value}%`,
46+
})
47+
}
48+
}
49+
}
50+
}

src/lib/query-builder/index.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import _ from 'lodash'
2+
import { ObjectLiteral } from 'typeorm'
3+
import { validate } from '../validate'
4+
import { applyFilter } from './filtered'
5+
import { applyPagination } from './pagination'
6+
import { applySort } from './sorted'
7+
import { QueryBuilderParams } from './types'
8+
9+
export function QueryBuilder<T extends ObjectLiteral>({ params, options }: QueryBuilderParams<T>) {
10+
const { query, model, reqQuery, options: opt } = params
11+
12+
const queryPage = _.get(reqQuery, 'page', 1)
13+
const queryPageSize = _.get(reqQuery, 'pageSize', 10)
14+
const queryFilters = _.get(reqQuery, 'filtered', [])
15+
const querySorts = _.get(reqQuery, 'sorted', [])
16+
17+
const limit = _.get(opt, 'limit', 100)
18+
const orderKey = _.get(opt, 'orderKey', 'created_at')
19+
20+
applyFilter({ query, filters: queryFilters, model, options })
21+
22+
applySort({
23+
query,
24+
sorts: querySorts,
25+
model,
26+
orderKey,
27+
})
28+
29+
applyPagination({
30+
query,
31+
page: validate.number(queryPage),
32+
pageSize: validate.number(queryPageSize),
33+
limit,
34+
})
35+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { ObjectLiteral } from 'typeorm'
2+
import { validate } from '../validate'
3+
import { ApplyPaginationParams, CalculatePageSizeParams } from './types'
4+
5+
function _calculatePageSize({ pageSize, limit }: CalculatePageSizeParams) {
6+
const min = 10
7+
const parsePageSize = validate.number(pageSize)
8+
9+
if (parsePageSize > 0) {
10+
return Math.min(parsePageSize, limit)
11+
}
12+
13+
return min
14+
}
15+
16+
export function applyPagination<T extends ObjectLiteral>({
17+
query,
18+
page,
19+
pageSize,
20+
limit = 100,
21+
}: ApplyPaginationParams<T>) {
22+
const parsePage = validate.number(page) || 1
23+
let parsePageSize = _calculatePageSize({ pageSize, limit })
24+
25+
if (parsePageSize <= 0) {
26+
parsePageSize = 10
27+
}
28+
29+
query.skip((parsePage - 1) * parsePageSize)
30+
query.take(parsePageSize)
31+
}

src/lib/query-builder/sorted.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { ObjectLiteral } from 'typeorm'
2+
import { ApplySortParams, QuerySorts } from './types'
3+
4+
export function applySort<T extends ObjectLiteral>({
5+
query,
6+
sorts,
7+
model,
8+
orderKey,
9+
}: ApplySortParams<T>) {
10+
let sorted: QuerySorts[] = []
11+
12+
if (Array.isArray(sorts)) {
13+
sorted = sorts
14+
} else {
15+
sorted = JSON.parse(sorts) as QuerySorts[]
16+
}
17+
18+
if (sorted.length > 0) {
19+
for (let i = 0; i < sorted.length; i += 1) {
20+
const item = sorted[i]
21+
query.addOrderBy(`${model}.${item.sort}`, item.order)
22+
}
23+
} else {
24+
query.orderBy(`${model}.${orderKey || 'created_at'}`, 'DESC')
25+
}
26+
}

src/lib/query-builder/types.ts

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import { DataSourceOptions, ObjectLiteral, SelectQueryBuilder } from 'typeorm'
2+
3+
export type CalculatePageSizeParams = {
4+
pageSize: number
5+
limit: number
6+
}
7+
8+
export type ApplyPaginationParams<T extends ObjectLiteral> = {
9+
query: SelectQueryBuilder<T>
10+
page: number
11+
pageSize: number
12+
limit?: number
13+
}
14+
15+
export type ApplyFilterParams<T extends ObjectLiteral> = {
16+
query: SelectQueryBuilder<T>
17+
filters: any
18+
model: string
19+
options?: DataSourceOptions
20+
}
21+
22+
export type QueryFilters = {
23+
id: string
24+
value: string
25+
}
26+
27+
export type ApplySortParams<T extends ObjectLiteral> = {
28+
query: SelectQueryBuilder<T>
29+
sorts: any
30+
model: string
31+
orderKey?: string
32+
}
33+
34+
export type QuerySorts = {
35+
sort: string
36+
order: 'ASC' | 'DESC'
37+
}
38+
39+
type RequestQuery = {
40+
filtered?: QueryFilters[]
41+
sorted?: QuerySorts[]
42+
page?: string | number
43+
pageSize?: string | number
44+
[key: string]: any
45+
}
46+
47+
type QueryOptions = {
48+
limit?: number
49+
orderKey?: string
50+
}
51+
52+
export type QueryParams<T extends ObjectLiteral> = {
53+
model: string
54+
query: SelectQueryBuilder<T>
55+
reqQuery: RequestQuery
56+
options?: QueryOptions
57+
}
58+
59+
export type QueryBuilderParams<T extends ObjectLiteral> = {
60+
params: QueryParams<T>
61+
options?: DataSourceOptions
62+
}

0 commit comments

Comments
 (0)