sonamu 0.5.5 → 0.5.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api/decorators.d.ts +1 -0
- package/dist/api/decorators.d.ts.map +1 -1
- package/dist/api/decorators.js +1 -1
- package/dist/api/decorators.js.map +1 -1
- package/dist/api/sonamu.d.ts.map +1 -1
- package/dist/api/sonamu.js +1 -1
- package/dist/api/sonamu.js.map +1 -1
- package/dist/bin/build-config.d.ts +4 -0
- package/dist/bin/build-config.d.ts.map +1 -1
- package/dist/bin/build-config.js +1 -1
- package/dist/bin/build-config.js.map +1 -1
- package/dist/bin/cli-wrapper.js +1 -1
- package/dist/bin/cli-wrapper.js.map +1 -1
- package/dist/database/db.d.ts +3 -0
- package/dist/database/db.d.ts.map +1 -1
- package/dist/database/db.js +1 -1
- package/dist/database/db.js.map +1 -1
- package/dist/database/puri-wrapper.d.ts +22 -10
- package/dist/database/puri-wrapper.d.ts.map +1 -1
- package/dist/database/puri-wrapper.js +1 -1
- package/dist/database/puri-wrapper.js.map +1 -1
- package/dist/database/puri.d.ts +91 -65
- package/dist/database/puri.d.ts.map +1 -1
- package/dist/database/puri.js +1 -1
- package/dist/database/puri.js.map +1 -1
- package/dist/database/puri.types.d.ts +28 -42
- package/dist/database/puri.types.d.ts.map +1 -1
- package/dist/database/transaction-context.d.ts +3 -3
- package/dist/database/transaction-context.d.ts.map +1 -1
- package/dist/database/transaction-context.js.map +1 -1
- package/dist/templates/service.template.d.ts.map +1 -1
- package/dist/templates/service.template.js +1 -1
- package/dist/templates/service.template.js.map +1 -1
- package/dist/types/types.d.ts +1 -1
- package/dist/types/types.d.ts.map +1 -1
- package/dist/types/types.js.map +1 -1
- package/package.json +1 -2
- package/src/api/decorators.ts +14 -5
- package/src/api/sonamu.ts +21 -20
- package/src/bin/build-config.ts +7 -3
- package/src/bin/cli-wrapper.ts +31 -12
- package/src/database/db.ts +44 -9
- package/src/database/puri-wrapper.ts +104 -26
- package/src/database/puri.ts +434 -543
- package/src/database/puri.types.ts +99 -200
- package/src/database/transaction-context.ts +4 -4
- package/src/templates/service.template.ts +10 -1
- package/src/types/types.ts +1 -1
- package/dist/entity/migrator.d.ts +0 -135
- package/dist/entity/migrator.d.ts.map +0 -1
- package/dist/entity/migrator.js +0 -2
- package/dist/entity/migrator.js.map +0 -1
package/src/database/puri.ts
CHANGED
|
@@ -1,137 +1,144 @@
|
|
|
1
1
|
import type { Knex } from "knex";
|
|
2
2
|
import type {
|
|
3
3
|
AvailableColumns,
|
|
4
|
+
SelectObject,
|
|
5
|
+
ParseSelectObject,
|
|
6
|
+
WhereCondition,
|
|
4
7
|
ComparisonOperator,
|
|
5
|
-
EmptyRecord,
|
|
6
|
-
Expand,
|
|
7
8
|
ExtractColumnType,
|
|
9
|
+
SqlExpression,
|
|
10
|
+
Expand,
|
|
8
11
|
FulltextColumns,
|
|
9
|
-
InsertData,
|
|
10
|
-
MergeJoined,
|
|
11
|
-
ParseSelectObject,
|
|
12
12
|
ResultAvailableColumns,
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
WhereCondition,
|
|
13
|
+
InsertData,
|
|
14
|
+
SingleTableValue,
|
|
16
15
|
} from "./puri.types";
|
|
17
16
|
import chalk from "chalk";
|
|
17
|
+
import assert from "assert";
|
|
18
18
|
|
|
19
|
-
// 메인 Puri 클래스
|
|
20
19
|
export class Puri<
|
|
21
20
|
TSchema,
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
TJoined = EmptyRecord,
|
|
21
|
+
TTables extends Record<string, any>,
|
|
22
|
+
TResult,
|
|
23
|
+
TResolved = Expand<TResult>[],
|
|
26
24
|
> {
|
|
27
25
|
private knexQuery: Knex.QueryBuilder;
|
|
28
26
|
|
|
29
27
|
// 생성자 시그니처들
|
|
28
|
+
constructor(knex: Knex, tableName: string);
|
|
30
29
|
constructor(
|
|
31
30
|
knex: Knex,
|
|
32
|
-
|
|
33
|
-
);
|
|
34
|
-
constructor(
|
|
35
|
-
knex: Knex,
|
|
36
|
-
subquery: Puri<TSchema, any, any, TOriginal, any>,
|
|
37
|
-
alias: TTable extends string ? TTable : never
|
|
31
|
+
tableSpec: Record<string, string | Puri<TSchema, any, any>>
|
|
38
32
|
);
|
|
39
33
|
constructor(
|
|
40
34
|
private knex: Knex,
|
|
41
|
-
|
|
42
|
-
alias?: TTable extends string ? TTable : never
|
|
35
|
+
tableNameOrSpec: any
|
|
43
36
|
) {
|
|
44
|
-
if (typeof
|
|
45
|
-
//
|
|
46
|
-
this.knexQuery = knex(
|
|
37
|
+
if (typeof tableNameOrSpec === "string") {
|
|
38
|
+
// Case: new Puri(knex, "users")
|
|
39
|
+
this.knexQuery = this.knex(tableNameOrSpec).from(tableNameOrSpec);
|
|
40
|
+
} else if (typeof tableNameOrSpec === "object") {
|
|
41
|
+
const entries = Object.entries(tableNameOrSpec);
|
|
42
|
+
if (entries.length !== 1) {
|
|
43
|
+
throw new Error("Table spec must have exactly one entry");
|
|
44
|
+
}
|
|
45
|
+
assert(entries[0]);
|
|
46
|
+
const [alias, spec] = entries[0];
|
|
47
|
+
if (typeof spec === "string") {
|
|
48
|
+
this.knexQuery = this.knex(spec).from({ [alias]: spec });
|
|
49
|
+
} else if (spec instanceof Puri) {
|
|
50
|
+
const subqueryBuilder = spec.raw();
|
|
51
|
+
this.knexQuery = this.knex.from(subqueryBuilder.as(alias));
|
|
52
|
+
} else {
|
|
53
|
+
throw new Error("Invalid table specification");
|
|
54
|
+
}
|
|
47
55
|
} else {
|
|
48
|
-
|
|
49
|
-
this.knexQuery = knex.from(tableNameOrSubquery.raw().as(alias));
|
|
56
|
+
throw new Error("Invalid table specification");
|
|
50
57
|
}
|
|
51
58
|
}
|
|
52
59
|
|
|
53
|
-
// Static SQL helper functions
|
|
54
|
-
static count(column: string = "*"):
|
|
60
|
+
// Static SQL helper functions for SELECT
|
|
61
|
+
static count(column: string = "*"): SqlExpression<"number"> {
|
|
55
62
|
return {
|
|
56
|
-
_type: "
|
|
63
|
+
_type: "sql_expression",
|
|
57
64
|
_return: "number",
|
|
58
65
|
_sql: `COUNT(${column})`,
|
|
59
66
|
};
|
|
60
67
|
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
68
|
+
static sum(column: string): SqlExpression<"number"> {
|
|
69
|
+
return {
|
|
70
|
+
_type: "sql_expression",
|
|
71
|
+
_return: "number",
|
|
72
|
+
_sql: `SUM(${column})`,
|
|
73
|
+
};
|
|
64
74
|
}
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
75
|
+
static avg(column: string): SqlExpression<"number"> {
|
|
76
|
+
return {
|
|
77
|
+
_type: "sql_expression",
|
|
78
|
+
_return: "number",
|
|
79
|
+
_sql: `AVG(${column})`,
|
|
80
|
+
};
|
|
68
81
|
}
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
82
|
+
static max(column: string): SqlExpression<"number"> {
|
|
83
|
+
return {
|
|
84
|
+
_type: "sql_expression",
|
|
85
|
+
_return: "number",
|
|
86
|
+
_sql: `MAX(${column})`,
|
|
87
|
+
};
|
|
72
88
|
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
89
|
+
static min(column: string): SqlExpression<"number"> {
|
|
90
|
+
return {
|
|
91
|
+
_type: "sql_expression",
|
|
92
|
+
_return: "number",
|
|
93
|
+
_sql: `MIN(${column})`,
|
|
94
|
+
};
|
|
76
95
|
}
|
|
77
|
-
|
|
78
|
-
static concat(...args: string[]): SqlFunction<"string"> {
|
|
96
|
+
static concat(...args: string[]): SqlExpression<"string"> {
|
|
79
97
|
return {
|
|
80
|
-
_type: "
|
|
98
|
+
_type: "sql_expression",
|
|
81
99
|
_return: "string",
|
|
82
100
|
_sql: `CONCAT(${args.join(", ")})`,
|
|
83
101
|
};
|
|
84
102
|
}
|
|
85
|
-
|
|
86
|
-
static upper(column: string): SqlFunction<"string"> {
|
|
103
|
+
static upper(column: string): SqlExpression<"string"> {
|
|
87
104
|
return {
|
|
88
|
-
_type: "
|
|
105
|
+
_type: "sql_expression",
|
|
89
106
|
_return: "string",
|
|
90
107
|
_sql: `UPPER(${column})`,
|
|
91
108
|
};
|
|
92
109
|
}
|
|
93
|
-
|
|
94
|
-
static lower(column: string): SqlFunction<"string"> {
|
|
110
|
+
static lower(column: string): SqlExpression<"string"> {
|
|
95
111
|
return {
|
|
96
|
-
_type: "
|
|
112
|
+
_type: "sql_expression",
|
|
97
113
|
_return: "string",
|
|
98
114
|
_sql: `LOWER(${column})`,
|
|
99
115
|
};
|
|
100
116
|
}
|
|
101
117
|
|
|
102
|
-
// Raw functions
|
|
103
|
-
static rawString(sql: string):
|
|
104
|
-
return { _type: "
|
|
118
|
+
// Raw functions for SELECT
|
|
119
|
+
static rawString(sql: string): SqlExpression<"string"> {
|
|
120
|
+
return { _type: "sql_expression", _return: "string", _sql: sql };
|
|
105
121
|
}
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
return { _type: "sql_function", _return: "number", _sql: sql };
|
|
122
|
+
static rawNumber(sql: string): SqlExpression<"number"> {
|
|
123
|
+
return { _type: "sql_expression", _return: "number", _sql: sql };
|
|
109
124
|
}
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
return { _type: "sql_function", _return: "boolean", _sql: sql };
|
|
125
|
+
static rawBoolean(sql: string): SqlExpression<"boolean"> {
|
|
126
|
+
return { _type: "sql_expression", _return: "boolean", _sql: sql };
|
|
113
127
|
}
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
return { _type: "sql_function", _return: "date", _sql: sql };
|
|
128
|
+
static rawDate(sql: string): SqlExpression<"date"> {
|
|
129
|
+
return { _type: "sql_expression", _return: "date", _sql: sql };
|
|
117
130
|
}
|
|
118
131
|
|
|
119
|
-
//
|
|
120
|
-
select<TSelect extends SelectObject<
|
|
132
|
+
// SELECT
|
|
133
|
+
select<TSelect extends SelectObject<TTables>>(
|
|
121
134
|
selectObj: TSelect
|
|
122
|
-
): Puri<
|
|
123
|
-
TSchema,
|
|
124
|
-
TTable,
|
|
125
|
-
TOriginal,
|
|
126
|
-
ParseSelectObject<TSchema, TTable, TSelect, TOriginal, TJoined>,
|
|
127
|
-
TJoined
|
|
128
|
-
> {
|
|
135
|
+
): Puri<TSchema, TTables, ParseSelectObject<TTables, TSelect>> {
|
|
129
136
|
const selectClauses: (string | Knex.Raw)[] = [];
|
|
130
137
|
|
|
131
138
|
for (const [alias, columnOrFunction] of Object.entries(selectObj)) {
|
|
132
139
|
if (
|
|
133
140
|
typeof columnOrFunction === "object" &&
|
|
134
|
-
columnOrFunction._type === "
|
|
141
|
+
columnOrFunction._type === "sql_expression"
|
|
135
142
|
) {
|
|
136
143
|
// SQL 함수인 경우
|
|
137
144
|
selectClauses.push(
|
|
@@ -154,50 +161,204 @@ export class Puri<
|
|
|
154
161
|
return this as any;
|
|
155
162
|
}
|
|
156
163
|
|
|
157
|
-
//
|
|
158
|
-
selectAll():
|
|
159
|
-
TSchema,
|
|
160
|
-
TTable,
|
|
161
|
-
TOriginal,
|
|
162
|
-
TTable extends keyof TSchema
|
|
163
|
-
? TSchema[TTable] & TJoined
|
|
164
|
-
: TResult & TJoined,
|
|
165
|
-
TJoined
|
|
166
|
-
> {
|
|
164
|
+
// SELECT *
|
|
165
|
+
selectAll(): this {
|
|
167
166
|
this.knexQuery.select("*");
|
|
168
167
|
return this as any;
|
|
169
168
|
}
|
|
170
169
|
|
|
171
|
-
//
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
170
|
+
// JOIN: 서브쿼리 + Alias
|
|
171
|
+
join<TJoinAlias extends string, TSubResult>(
|
|
172
|
+
tableSpec: { [K in TJoinAlias]: Puri<TSchema, any, TSubResult> },
|
|
173
|
+
left: AvailableColumns<TTables>,
|
|
174
|
+
right: `${TJoinAlias}.${keyof TSubResult & string}`
|
|
175
|
+
): Puri<
|
|
176
|
+
TSchema,
|
|
177
|
+
TTables & Record<TJoinAlias, TSubResult>, // 서브쿼리의 TResult
|
|
178
|
+
TResult
|
|
179
|
+
>;
|
|
180
|
+
// JOIN: 테이블 + Alias
|
|
181
|
+
join<TJoinTable extends keyof TSchema, TJoinAlias extends string>(
|
|
182
|
+
tableSpec: { [K in TJoinAlias]: TJoinTable },
|
|
183
|
+
left: AvailableColumns<TTables>,
|
|
184
|
+
right: `${TJoinAlias}.${keyof TSchema[TJoinTable] & string}`
|
|
185
|
+
): Puri<
|
|
186
|
+
TSchema,
|
|
187
|
+
TTables & Record<TJoinAlias, TSchema[TJoinTable]>, // TTables 확장!
|
|
188
|
+
TResult
|
|
189
|
+
>;
|
|
190
|
+
// JOIN: 테이블명
|
|
191
|
+
join<TJoinTable extends keyof TSchema>(
|
|
192
|
+
tableName: TJoinTable,
|
|
193
|
+
left: AvailableColumns<TTables>,
|
|
194
|
+
right: `${TJoinTable & string}.${keyof TSchema[TJoinTable] & string}`
|
|
195
|
+
): Puri<
|
|
196
|
+
TSchema,
|
|
197
|
+
TTables & Record<TJoinTable, TSchema[TJoinTable]>, // 테이블명이 키
|
|
198
|
+
TResult
|
|
199
|
+
>;
|
|
200
|
+
// JOIN: 서브쿼리 + Alias + 콜백
|
|
201
|
+
join<TJoinAlias extends string, TSubResult>(
|
|
202
|
+
tableSpec: { [K in TJoinAlias]: Puri<TSchema, any, TSubResult> },
|
|
203
|
+
callback: (
|
|
204
|
+
j: JoinClauseGroup<TTables, Record<TJoinAlias, TSubResult>>
|
|
205
|
+
) => void
|
|
206
|
+
): Puri<TSchema, TTables & Record<TJoinAlias, TSubResult>, TResult>;
|
|
207
|
+
// JOIN: 테이블 + Alias + 콜백
|
|
208
|
+
join<TJoinTable extends keyof TSchema, TJoinAlias extends string>(
|
|
209
|
+
tableSpec: { [K in TJoinAlias]: TJoinTable },
|
|
210
|
+
callback: (
|
|
211
|
+
j: JoinClauseGroup<TTables, Record<TJoinAlias, TSchema[TJoinTable]>>
|
|
212
|
+
) => void
|
|
213
|
+
): Puri<TSchema, TTables & Record<TJoinAlias, TSchema[TJoinTable]>, TResult>;
|
|
214
|
+
// JOIN: 테이블명 + 콜백
|
|
215
|
+
join<TJoinTable extends keyof TSchema>(
|
|
216
|
+
tableName: TJoinTable,
|
|
217
|
+
callback: (
|
|
218
|
+
j: JoinClauseGroup<TTables, Record<TJoinTable, TSchema[TJoinTable]>>
|
|
219
|
+
) => void
|
|
220
|
+
): Puri<TSchema, TTables & Record<TJoinTable, TSchema[TJoinTable]>, TResult>;
|
|
221
|
+
// JOIN 실제 구현
|
|
222
|
+
join(tableNameOrSpec: any, ...args: any[]): any {
|
|
223
|
+
return this.__commonJoin("join", tableNameOrSpec, ...args);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// LEFT JOIN: 서브쿼리 + Alias
|
|
227
|
+
leftJoin<TJoinAlias extends string, TSubResult>(
|
|
228
|
+
tableSpec: { [K in TJoinAlias]: Puri<TSchema, any, TSubResult> },
|
|
229
|
+
left: AvailableColumns<TTables>,
|
|
230
|
+
right: `${TJoinAlias}.${keyof TSubResult & string}`
|
|
231
|
+
): Puri<
|
|
232
|
+
TSchema,
|
|
233
|
+
TTables & Record<TJoinAlias, TSubResult>, // 서브쿼리의 TResult
|
|
234
|
+
TResult
|
|
235
|
+
>;
|
|
236
|
+
// LEFT JOIN: 테이블 + Alias
|
|
237
|
+
leftJoin<TJoinTable extends keyof TSchema, TJoinAlias extends string>(
|
|
238
|
+
tableSpec: { [K in TJoinAlias]: TJoinTable },
|
|
239
|
+
left: AvailableColumns<TTables>,
|
|
240
|
+
right: `${TJoinAlias}.${keyof TSchema[TJoinTable] & string}`
|
|
241
|
+
): Puri<
|
|
242
|
+
TSchema,
|
|
243
|
+
TTables & Record<TJoinAlias, TSchema[TJoinTable]>, // TTables 확장!
|
|
244
|
+
TResult
|
|
245
|
+
>;
|
|
246
|
+
// LEFT JOIN: 테이블명
|
|
247
|
+
leftJoin<TJoinTable extends keyof TSchema>(
|
|
248
|
+
tableName: TJoinTable,
|
|
249
|
+
left: AvailableColumns<TTables>,
|
|
250
|
+
right: `${TJoinTable & string}.${keyof TSchema[TJoinTable] & string}`
|
|
251
|
+
): Puri<
|
|
252
|
+
TSchema,
|
|
253
|
+
TTables & Record<TJoinTable, TSchema[TJoinTable]>, // 테이블명이 키
|
|
254
|
+
TResult
|
|
255
|
+
>;
|
|
256
|
+
// LEFT JOIN: 서브쿼리 + Alias + 콜백
|
|
257
|
+
leftJoin<TJoinAlias extends string, TSubResult>(
|
|
258
|
+
tableSpec: { [K in TJoinAlias]: Puri<TSchema, any, TSubResult> },
|
|
259
|
+
callback: (
|
|
260
|
+
j: JoinClauseGroup<TTables, Record<TJoinAlias, TSubResult>>
|
|
261
|
+
) => void
|
|
262
|
+
): Puri<TSchema, TTables & Record<TJoinAlias, TSubResult>, TResult>;
|
|
263
|
+
// LEFT JOIN: 테이블 + Alias + 콜백
|
|
264
|
+
leftJoin<TJoinTable extends keyof TSchema, TJoinAlias extends string>(
|
|
265
|
+
tableSpec: { [K in TJoinAlias]: TJoinTable },
|
|
266
|
+
callback: (
|
|
267
|
+
j: JoinClauseGroup<TTables, Record<TJoinAlias, TSchema[TJoinTable]>>
|
|
268
|
+
) => void
|
|
269
|
+
): Puri<TSchema, TTables & Record<TJoinAlias, TSchema[TJoinTable]>, TResult>;
|
|
270
|
+
// LEFT JOIN: 테이블명 + 콜백
|
|
271
|
+
leftJoin<TJoinTable extends keyof TSchema>(
|
|
272
|
+
tableName: TJoinTable,
|
|
273
|
+
callback: (
|
|
274
|
+
j: JoinClauseGroup<TTables, Record<TJoinTable, TSchema[TJoinTable]>>
|
|
275
|
+
) => void
|
|
276
|
+
): Puri<TSchema, TTables & Record<TJoinTable, TSchema[TJoinTable]>, TResult>;
|
|
277
|
+
// LEFT JOIN 실제 구현
|
|
278
|
+
leftJoin(tableNameOrSpec: any, ...args: any[]): any {
|
|
279
|
+
return this.__commonJoin("leftJoin", tableNameOrSpec, ...args);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
__commonJoin(
|
|
283
|
+
joinType: "join" | "leftJoin",
|
|
284
|
+
tableNameOrSpec: any,
|
|
285
|
+
...args: any[]
|
|
286
|
+
): this {
|
|
287
|
+
if (typeof tableNameOrSpec === "string") {
|
|
288
|
+
// Case 1: join("posts", ...)
|
|
289
|
+
const tableName = tableNameOrSpec;
|
|
290
|
+
|
|
291
|
+
if (args.length === 1 && typeof args[0] === "function") {
|
|
292
|
+
// join("posts", callback)
|
|
293
|
+
const callback = args[0];
|
|
294
|
+
this.knexQuery[joinType](tableName, (joinClause) => {
|
|
295
|
+
callback(new JoinClauseGroup(joinClause));
|
|
296
|
+
});
|
|
297
|
+
} else {
|
|
298
|
+
// join("posts", left, right)
|
|
299
|
+
const [left, right] = args;
|
|
300
|
+
this.knexQuery[joinType](tableName, left, right);
|
|
301
|
+
}
|
|
302
|
+
} else if (typeof tableNameOrSpec === "object") {
|
|
303
|
+
// Case 2: join({ alias: "table" }, ...) or join({ alias: subquery }, ...)
|
|
304
|
+
const entries = Object.entries(tableNameOrSpec);
|
|
305
|
+
if (entries.length !== 1) {
|
|
306
|
+
throw new Error("Table spec must have exactly one entry");
|
|
307
|
+
}
|
|
308
|
+
assert(entries[0]);
|
|
309
|
+
const [[alias, spec]] = entries;
|
|
310
|
+
|
|
311
|
+
if (typeof spec === "string") {
|
|
312
|
+
// 테이블: join({ p: "posts" }, ...)
|
|
313
|
+
if (args.length === 1 && typeof args[0] === "function") {
|
|
314
|
+
// Callback
|
|
315
|
+
const callback = args[0];
|
|
316
|
+
this.knexQuery[joinType]({ [alias]: spec }, (joinClause) => {
|
|
317
|
+
callback(new JoinClauseGroup(joinClause));
|
|
318
|
+
});
|
|
319
|
+
} else {
|
|
320
|
+
// Simple
|
|
321
|
+
const [left, right] = args;
|
|
322
|
+
this.knexQuery[joinType]({ [alias]: spec }, left, right);
|
|
323
|
+
}
|
|
324
|
+
} else if (spec instanceof Puri) {
|
|
325
|
+
// 서브쿼리: join({ sq: subquery }, ...)
|
|
326
|
+
if (args.length === 1 && typeof args[0] === "function") {
|
|
327
|
+
// Callback
|
|
328
|
+
const callback = args[0];
|
|
329
|
+
this.knexQuery[joinType](spec.raw().as(alias), (joinClause) => {
|
|
330
|
+
callback(new JoinClauseGroup(joinClause));
|
|
331
|
+
});
|
|
332
|
+
} else {
|
|
333
|
+
// Simple
|
|
334
|
+
const [left, right] = args;
|
|
335
|
+
this.knexQuery[joinType](spec.raw().as(alias), left, right);
|
|
336
|
+
}
|
|
337
|
+
} else {
|
|
338
|
+
throw new Error("Invalid table specification");
|
|
339
|
+
}
|
|
340
|
+
} else {
|
|
341
|
+
throw new Error("Invalid arguments");
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
return this;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
// WHERE: 객체 - 사용: .where({ "u.id": 1, "u.status": "active" })
|
|
348
|
+
where(conditions: WhereCondition<TTables>): this;
|
|
349
|
+
// WHERE: 컬럼 - 사용: .where("u.id", 1)
|
|
350
|
+
where<TColumn extends AvailableColumns<TTables>>(
|
|
176
351
|
column: TColumn,
|
|
177
|
-
value: ExtractColumnType<
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
TOriginal,
|
|
182
|
-
TJoined
|
|
183
|
-
>
|
|
184
|
-
): Puri<TSchema, TTable, TOriginal, TResult, TJoined>;
|
|
185
|
-
where<TColumn extends AvailableColumns<TSchema, TTable, TOriginal, TJoined>>(
|
|
352
|
+
value: ExtractColumnType<TTables, TColumn & string>
|
|
353
|
+
): this;
|
|
354
|
+
// WHERE: 컬럼 - 사용: .where("u.id", ">", 10)
|
|
355
|
+
where<TColumn extends AvailableColumns<TTables>>(
|
|
186
356
|
column: TColumn,
|
|
187
|
-
operator: ComparisonOperator
|
|
188
|
-
value: ExtractColumnType<
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
TOriginal,
|
|
193
|
-
TJoined
|
|
194
|
-
>
|
|
195
|
-
): Puri<TSchema, TTable, TOriginal, TResult, TJoined>;
|
|
196
|
-
where(
|
|
197
|
-
columnOrConditions: any,
|
|
198
|
-
operatorOrValue?: any,
|
|
199
|
-
value?: any
|
|
200
|
-
): Puri<TSchema, TTable, TOriginal, TResult, TJoined> {
|
|
357
|
+
operator: ComparisonOperator,
|
|
358
|
+
value: ExtractColumnType<TTables, TColumn & string>
|
|
359
|
+
): this;
|
|
360
|
+
// WHERE: 컬럼 - 사용: .where("u.id", "like", "%test%")
|
|
361
|
+
where(columnOrConditions: any, operatorOrValue?: any, value?: any): this {
|
|
201
362
|
if (typeof columnOrConditions === "object") {
|
|
202
363
|
this.knexQuery.where(columnOrConditions);
|
|
203
364
|
} else if (arguments.length === 2) {
|
|
@@ -223,335 +384,139 @@ export class Puri<
|
|
|
223
384
|
return this;
|
|
224
385
|
}
|
|
225
386
|
|
|
226
|
-
//
|
|
227
|
-
whereIn<
|
|
228
|
-
TColumn extends AvailableColumns<TSchema, TTable, TOriginal, TJoined>,
|
|
229
|
-
>(
|
|
387
|
+
// WHERE IN
|
|
388
|
+
whereIn<TColumn extends AvailableColumns<TTables>>(
|
|
230
389
|
column: TColumn,
|
|
231
|
-
values: ExtractColumnType<
|
|
232
|
-
|
|
233
|
-
TTable,
|
|
234
|
-
TColumn & string,
|
|
235
|
-
TOriginal,
|
|
236
|
-
TJoined
|
|
237
|
-
>[]
|
|
238
|
-
): Puri<TSchema, TTable, TOriginal, TResult, TJoined>;
|
|
239
|
-
whereIn(
|
|
240
|
-
column: string,
|
|
241
|
-
values: any[]
|
|
242
|
-
): Puri<TSchema, TTable, TOriginal, TResult, TJoined> {
|
|
390
|
+
values: ExtractColumnType<TTables, TColumn & string>[]
|
|
391
|
+
): Puri<TSchema, TTables, TResult> {
|
|
243
392
|
this.knexQuery.whereIn(column, values);
|
|
244
|
-
return this;
|
|
393
|
+
return this as any;
|
|
245
394
|
}
|
|
246
395
|
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
>(
|
|
396
|
+
// WHERE NOT IN
|
|
397
|
+
whereNotIn<TColumn extends AvailableColumns<TTables>>(
|
|
250
398
|
column: TColumn,
|
|
251
|
-
values: ExtractColumnType<
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
TOriginal,
|
|
256
|
-
TJoined
|
|
257
|
-
>[]
|
|
258
|
-
): Puri<TSchema, TTable, TOriginal, TResult, TJoined>;
|
|
259
|
-
whereNotIn(
|
|
260
|
-
column: string,
|
|
261
|
-
values: any[]
|
|
262
|
-
): Puri<TSchema, TTable, TOriginal, TResult, TJoined> {
|
|
263
|
-
this.knexQuery.whereNotIn(column, values);
|
|
264
|
-
return this;
|
|
399
|
+
values: ExtractColumnType<TTables, TColumn & string>[]
|
|
400
|
+
): Puri<TSchema, TTables, TResult> {
|
|
401
|
+
this.knexQuery.whereIn(column, values);
|
|
402
|
+
return this as any;
|
|
265
403
|
}
|
|
266
404
|
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
>(
|
|
405
|
+
// WHERE MATCH
|
|
406
|
+
whereMatch<TColumn extends FulltextColumns<TTables>>(
|
|
270
407
|
column: TColumn,
|
|
271
408
|
value: string
|
|
272
|
-
):
|
|
409
|
+
): this {
|
|
273
410
|
this.knexQuery.whereRaw(`MATCH (${String(column)}) AGAINST (?)`, [value]);
|
|
274
411
|
return this;
|
|
275
412
|
}
|
|
276
413
|
|
|
277
|
-
//
|
|
278
|
-
whereGroup(
|
|
279
|
-
callback: (
|
|
280
|
-
group: WhereGroup<TSchema, TTable, TOriginal, TJoined>
|
|
281
|
-
) => WhereGroup<TSchema, TTable, TOriginal, TJoined>
|
|
282
|
-
): Puri<TSchema, TTable, TOriginal, TResult, TJoined> {
|
|
414
|
+
// WHERE 괄호 그룹핑
|
|
415
|
+
whereGroup(callback: (g: WhereGroup<TTables>) => void): this {
|
|
283
416
|
this.knexQuery.where((builder) => {
|
|
284
|
-
const group = new WhereGroup<
|
|
285
|
-
builder
|
|
286
|
-
);
|
|
417
|
+
const group = new WhereGroup<TTables>(builder);
|
|
287
418
|
callback(group);
|
|
288
419
|
});
|
|
289
420
|
return this;
|
|
290
421
|
}
|
|
291
|
-
|
|
292
|
-
orWhereGroup(
|
|
293
|
-
callback: (
|
|
294
|
-
group: WhereGroup<TSchema, TTable, TOriginal, TJoined>
|
|
295
|
-
) => WhereGroup<TSchema, TTable, TOriginal, TJoined>
|
|
296
|
-
): Puri<TSchema, TTable, TOriginal, TResult, TJoined> {
|
|
422
|
+
orWhereGroup(callback: (g: WhereGroup<TTables>) => void): this {
|
|
297
423
|
this.knexQuery.orWhere((builder) => {
|
|
298
|
-
const group = new WhereGroup<
|
|
299
|
-
builder
|
|
300
|
-
);
|
|
424
|
+
const group = new WhereGroup<TTables>(builder);
|
|
301
425
|
callback(group);
|
|
302
426
|
});
|
|
303
427
|
return this;
|
|
304
428
|
}
|
|
305
429
|
|
|
306
|
-
//
|
|
307
|
-
|
|
308
|
-
TJoinTable extends keyof TSchema,
|
|
309
|
-
TLColumn extends AvailableColumns<
|
|
310
|
-
TSchema,
|
|
311
|
-
TTable,
|
|
312
|
-
TOriginal,
|
|
313
|
-
TJoined & Record<TJoinTable, TSchema[TJoinTable]>
|
|
314
|
-
>,
|
|
315
|
-
TRColumn extends AvailableColumns<
|
|
316
|
-
TSchema,
|
|
317
|
-
TTable,
|
|
318
|
-
TOriginal,
|
|
319
|
-
TJoined & Record<TJoinTable, TSchema[TJoinTable]>
|
|
320
|
-
>,
|
|
321
|
-
>(
|
|
322
|
-
table: TJoinTable,
|
|
323
|
-
left: TLColumn,
|
|
324
|
-
right: TRColumn
|
|
325
|
-
): Puri<
|
|
326
|
-
TSchema,
|
|
327
|
-
TTable,
|
|
328
|
-
TOriginal,
|
|
329
|
-
TResult,
|
|
330
|
-
MergeJoined<TJoined, Record<TJoinTable, TSchema[TJoinTable]>>
|
|
331
|
-
>;
|
|
332
|
-
join<TJoinTable extends keyof TSchema>(
|
|
333
|
-
table: TJoinTable,
|
|
334
|
-
joinCallback: (
|
|
335
|
-
joinClause: JoinClauseGroup<TSchema, TTable, TOriginal, TJoined>
|
|
336
|
-
) => void
|
|
337
|
-
): Puri<
|
|
338
|
-
TSchema,
|
|
339
|
-
TTable,
|
|
340
|
-
TOriginal,
|
|
341
|
-
TResult,
|
|
342
|
-
MergeJoined<TJoined, Record<TJoinTable, TSchema[TJoinTable]>>
|
|
343
|
-
>;
|
|
344
|
-
join<TSubResult, TAlias extends string>(
|
|
345
|
-
subquery: Puri<TSchema, any, any, TSubResult, any>,
|
|
346
|
-
alias: TAlias,
|
|
347
|
-
left: string,
|
|
348
|
-
right: string
|
|
349
|
-
): Puri<
|
|
350
|
-
TSchema,
|
|
351
|
-
TTable,
|
|
352
|
-
TOriginal,
|
|
353
|
-
TResult,
|
|
354
|
-
TJoined & Record<TAlias, TSubResult>
|
|
355
|
-
>;
|
|
356
|
-
join(
|
|
357
|
-
table: string,
|
|
358
|
-
left: string,
|
|
359
|
-
right: string
|
|
360
|
-
): Puri<TSchema, TTable, TOriginal, TResult, TJoined>;
|
|
361
|
-
join(
|
|
362
|
-
tableOrSubquery: string | keyof TSchema | Puri<TSchema, any, any, any, any>,
|
|
363
|
-
...args: any[]
|
|
364
|
-
): Puri<TSchema, TTable, TOriginal, TResult, any> {
|
|
365
|
-
if (tableOrSubquery instanceof Puri) {
|
|
366
|
-
// 서브쿼리 조인: join(subquery, alias, left, right)
|
|
367
|
-
const [alias, left, right] = args;
|
|
368
|
-
this.knexQuery.join(tableOrSubquery.raw().as(alias), left, right);
|
|
369
|
-
} else if (
|
|
370
|
-
args.length === 2 &&
|
|
371
|
-
typeof args[0] === "string" &&
|
|
372
|
-
typeof args[1] === "string"
|
|
373
|
-
) {
|
|
374
|
-
const [left, right] = args;
|
|
375
|
-
this.knexQuery.join(tableOrSubquery as string, left, right);
|
|
376
|
-
} else if (args.length === 1 && typeof args[0] === "function") {
|
|
377
|
-
const joinCallback = args[0];
|
|
378
|
-
this.knexQuery.join(tableOrSubquery as string, (joinClause) => {
|
|
379
|
-
joinCallback(new JoinClauseGroup(joinClause));
|
|
380
|
-
});
|
|
381
|
-
} else {
|
|
382
|
-
throw new Error("Invalid arguments");
|
|
383
|
-
}
|
|
384
|
-
return this as any;
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
leftJoin<
|
|
388
|
-
TJoinTable extends keyof TSchema,
|
|
389
|
-
TLColumn extends AvailableColumns<
|
|
390
|
-
TSchema,
|
|
391
|
-
TTable,
|
|
392
|
-
TOriginal,
|
|
393
|
-
TJoined & Record<TJoinTable, TSchema[TJoinTable]>
|
|
394
|
-
>,
|
|
395
|
-
TRColumn extends AvailableColumns<
|
|
396
|
-
TSchema,
|
|
397
|
-
TTable,
|
|
398
|
-
TOriginal,
|
|
399
|
-
TJoined & Record<TJoinTable, TSchema[TJoinTable]>
|
|
400
|
-
>,
|
|
401
|
-
>(
|
|
402
|
-
table: TJoinTable,
|
|
403
|
-
left: TLColumn,
|
|
404
|
-
right: TRColumn
|
|
405
|
-
): Puri<
|
|
406
|
-
TSchema,
|
|
407
|
-
TTable,
|
|
408
|
-
TOriginal,
|
|
409
|
-
TResult,
|
|
410
|
-
TJoined & Record<TJoinTable, Partial<TSchema[TJoinTable]>>
|
|
411
|
-
>;
|
|
412
|
-
leftJoin<TSubResult, TAlias extends string>(
|
|
413
|
-
subquery: Puri<TSchema, any, any, TSubResult, any>,
|
|
414
|
-
alias: TAlias,
|
|
415
|
-
left: string,
|
|
416
|
-
right: string
|
|
417
|
-
): Puri<
|
|
418
|
-
TSchema,
|
|
419
|
-
TTable,
|
|
420
|
-
TOriginal,
|
|
421
|
-
TResult,
|
|
422
|
-
TJoined & Record<TAlias, Partial<TSubResult>>
|
|
423
|
-
>;
|
|
424
|
-
leftJoin(
|
|
425
|
-
table: string,
|
|
426
|
-
left: string,
|
|
427
|
-
right: string
|
|
428
|
-
): Puri<TSchema, TTable, TOriginal, TResult, TJoined>;
|
|
429
|
-
leftJoin(
|
|
430
|
-
tableOrSubquery: string | keyof TSchema | Puri<TSchema, any, any, any, any>,
|
|
431
|
-
...args: any[]
|
|
432
|
-
): Puri<TSchema, TTable, TOriginal, TResult, any> {
|
|
433
|
-
if (tableOrSubquery instanceof Puri) {
|
|
434
|
-
// 서브쿼리 조인: leftJoin(subquery, alias, left, right)
|
|
435
|
-
const [alias, left, right] = args;
|
|
436
|
-
this.knexQuery.leftJoin(tableOrSubquery.raw().as(alias), left, right);
|
|
437
|
-
} else {
|
|
438
|
-
const [left, right] = args;
|
|
439
|
-
this.knexQuery.leftJoin(tableOrSubquery as string, left, right);
|
|
440
|
-
}
|
|
441
|
-
return this as any;
|
|
442
|
-
}
|
|
443
|
-
|
|
444
|
-
// OrderBy
|
|
445
|
-
orderBy<
|
|
446
|
-
TColumn extends ResultAvailableColumns<
|
|
447
|
-
TSchema,
|
|
448
|
-
TTable,
|
|
449
|
-
TOriginal,
|
|
450
|
-
TResult,
|
|
451
|
-
TJoined
|
|
452
|
-
>,
|
|
453
|
-
>(
|
|
430
|
+
// ORDER BY
|
|
431
|
+
orderBy<TColumn extends ResultAvailableColumns<TTables, TResult>>(
|
|
454
432
|
column: TColumn,
|
|
455
433
|
direction: "asc" | "desc"
|
|
456
|
-
):
|
|
457
|
-
orderBy(
|
|
458
|
-
column: string,
|
|
459
|
-
direction: "asc" | "desc" = "asc"
|
|
460
|
-
): Puri<TSchema, TTable, TOriginal, TResult, TJoined> {
|
|
434
|
+
): this;
|
|
435
|
+
orderBy(column: string, direction: "asc" | "desc" = "asc"): this {
|
|
461
436
|
this.knexQuery.orderBy(column, direction);
|
|
462
437
|
return this;
|
|
463
438
|
}
|
|
464
439
|
|
|
465
440
|
// 기본 쿼리 메서드들
|
|
466
|
-
limit(count: number):
|
|
441
|
+
limit(count: number): this {
|
|
467
442
|
this.knexQuery.limit(count);
|
|
468
443
|
return this;
|
|
469
444
|
}
|
|
470
445
|
|
|
471
|
-
offset(count: number):
|
|
446
|
+
offset(count: number): this {
|
|
472
447
|
this.knexQuery.offset(count);
|
|
473
448
|
return this;
|
|
474
449
|
}
|
|
475
450
|
|
|
476
|
-
//
|
|
477
|
-
groupBy<
|
|
478
|
-
TColumns
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
TOriginal,
|
|
482
|
-
TResult,
|
|
483
|
-
TJoined
|
|
484
|
-
>,
|
|
485
|
-
>(...columns: TColumns[]): Puri<TSchema, TTable, TOriginal, TResult, TJoined>;
|
|
486
|
-
groupBy(
|
|
487
|
-
...columns: string[]
|
|
488
|
-
): Puri<TSchema, TTable, TOriginal, TResult, TJoined> {
|
|
451
|
+
// GROUP BY
|
|
452
|
+
groupBy<TColumns extends ResultAvailableColumns<TTables, TResult>>(
|
|
453
|
+
...columns: TColumns[]
|
|
454
|
+
): this;
|
|
455
|
+
groupBy(...columns: string[]): this {
|
|
489
456
|
this.knexQuery.groupBy(...(columns as string[]));
|
|
490
457
|
return this;
|
|
491
458
|
}
|
|
492
459
|
|
|
493
|
-
|
|
494
|
-
having
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
TTable,
|
|
498
|
-
TOriginal,
|
|
499
|
-
TResult,
|
|
500
|
-
TJoined
|
|
501
|
-
>,
|
|
502
|
-
>(
|
|
503
|
-
condition: TColumn,
|
|
460
|
+
// HAVING
|
|
461
|
+
having(condition: string): this;
|
|
462
|
+
having<TColumn extends ResultAvailableColumns<TTables, TResult>>(
|
|
463
|
+
column: TColumn,
|
|
504
464
|
operator: ComparisonOperator,
|
|
505
465
|
value: any
|
|
506
|
-
):
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
466
|
+
): this;
|
|
467
|
+
// HAVING 구현
|
|
468
|
+
having(...conditions: any[]): this {
|
|
469
|
+
if (conditions.length === 1) {
|
|
470
|
+
// having("COUNT(*) > 10")
|
|
471
|
+
this.knexQuery.having(conditions[0]);
|
|
472
|
+
} else if (conditions.length === 3) {
|
|
473
|
+
// having("count", ">", 10)
|
|
474
|
+
this.knexQuery.having(conditions[0], conditions[1], conditions[2]);
|
|
475
|
+
} else {
|
|
476
|
+
throw new Error("Invalid having arguments");
|
|
477
|
+
}
|
|
511
478
|
return this;
|
|
512
479
|
}
|
|
480
|
+
|
|
513
481
|
// 실행 메서드들 - thenable 구현
|
|
514
|
-
then<TResult1, TResult2 = never>(
|
|
482
|
+
then<TResult1 = TResolved, TResult2 = never>(
|
|
515
483
|
onfulfilled?:
|
|
516
|
-
| ((
|
|
517
|
-
value: Expand<TResult>[]
|
|
518
|
-
) => Expand<TResult1> | PromiseLike<Expand<TResult1>>)
|
|
484
|
+
| ((value: TResolved) => TResult1 | PromiseLike<TResult1>)
|
|
519
485
|
| null,
|
|
520
486
|
onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null
|
|
521
|
-
): Promise<
|
|
487
|
+
): Promise<TResult1 | TResult2> {
|
|
522
488
|
return this.knexQuery.then(onfulfilled as any, onrejected);
|
|
523
489
|
}
|
|
524
|
-
|
|
525
490
|
catch<TResult2 = never>(
|
|
526
491
|
onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null
|
|
527
492
|
): Promise<Expand<TResult> | TResult2> {
|
|
528
493
|
return this.knexQuery.catch(onrejected);
|
|
529
494
|
}
|
|
530
|
-
|
|
531
495
|
finally(onfinally?: (() => void) | null): Promise<Expand<TResult>> {
|
|
532
496
|
return this.knexQuery.finally(onfinally);
|
|
533
497
|
}
|
|
534
498
|
|
|
535
|
-
//
|
|
499
|
+
// 하나만 쿼리
|
|
536
500
|
async first(): Promise<Expand<TResult> | undefined> {
|
|
537
|
-
return this.knexQuery.first()
|
|
501
|
+
return this.knexQuery.first();
|
|
538
502
|
}
|
|
539
|
-
|
|
503
|
+
// 하나만 쿼리 실패 시 에러
|
|
540
504
|
async firstOrFail(): Promise<TResult> {
|
|
541
505
|
const result = await this.knexQuery.first();
|
|
542
506
|
if (!result) {
|
|
543
507
|
throw new Error("No results found");
|
|
544
508
|
}
|
|
545
|
-
return result
|
|
509
|
+
return result;
|
|
546
510
|
}
|
|
547
511
|
|
|
512
|
+
// 쿼리 후 인덱스 리턴
|
|
548
513
|
async at(index: number): Promise<Expand<TResult> | undefined> {
|
|
549
|
-
const results = await this;
|
|
550
|
-
return results[index]
|
|
514
|
+
const results = (await this) as any[];
|
|
515
|
+
return results[index];
|
|
551
516
|
}
|
|
552
|
-
|
|
517
|
+
// 쿼리 후 인덱스 리턴 실패 시 에러
|
|
553
518
|
async assertAt(index: number): Promise<Expand<TResult>> {
|
|
554
|
-
const results = await this;
|
|
519
|
+
const results = (await this) as any[];
|
|
555
520
|
const result = results[index];
|
|
556
521
|
if (result === undefined) {
|
|
557
522
|
throw new Error(`No result found at index ${index}`);
|
|
@@ -559,31 +524,67 @@ export class Puri<
|
|
|
559
524
|
return result;
|
|
560
525
|
}
|
|
561
526
|
|
|
562
|
-
//
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
527
|
+
// 쿼리한 레코드에서 특정 컬럼만 추출한 배열 리턴
|
|
528
|
+
async pluck<TColumn extends ResultAvailableColumns<TTables, TResult>>(
|
|
529
|
+
column: TColumn
|
|
530
|
+
): Promise<ExtractColumnType<TTables, TColumn & string>[]> {
|
|
531
|
+
return this.knexQuery.pluck(column) as Promise<
|
|
532
|
+
ExtractColumnType<TTables, TColumn & string>[]
|
|
533
|
+
>;
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
// INSERT
|
|
537
|
+
insert(
|
|
538
|
+
data: InsertData<SingleTableValue<TTables>>
|
|
539
|
+
): Puri<TSchema, {}, number, number[]> {
|
|
540
|
+
this.knexQuery.insert(data);
|
|
541
|
+
return this as any;
|
|
568
542
|
}
|
|
569
543
|
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
return this
|
|
544
|
+
// UPDATE
|
|
545
|
+
update(data: WhereCondition<TTables>): Puri<TSchema, {}, number, number> {
|
|
546
|
+
this.knexQuery.update(data);
|
|
547
|
+
return this as any;
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
// Increment
|
|
551
|
+
increment<TColumn extends AvailableColumns<TTables>>(
|
|
552
|
+
column: TColumn,
|
|
553
|
+
value: number
|
|
554
|
+
): this {
|
|
555
|
+
if (value <= 0) {
|
|
556
|
+
throw new Error("Increment value must be greater than 0");
|
|
557
|
+
}
|
|
558
|
+
this.knexQuery.increment(column, value);
|
|
559
|
+
return this;
|
|
560
|
+
}
|
|
561
|
+
// Decrement
|
|
562
|
+
decrement<TColumn extends AvailableColumns<TTables>>(
|
|
563
|
+
column: TColumn,
|
|
564
|
+
value: number
|
|
565
|
+
): this {
|
|
566
|
+
if (value <= 0) {
|
|
567
|
+
throw new Error("Decrement value must be greater than 0");
|
|
568
|
+
}
|
|
569
|
+
this.knexQuery.decrement(column, value);
|
|
570
|
+
return this;
|
|
574
571
|
}
|
|
575
572
|
|
|
576
|
-
|
|
577
|
-
|
|
573
|
+
// DELETE
|
|
574
|
+
delete(): Puri<TSchema, {}, number, number> {
|
|
575
|
+
this.knexQuery.delete();
|
|
576
|
+
return this as any;
|
|
578
577
|
}
|
|
579
578
|
|
|
579
|
+
// 확인 쿼리 리턴
|
|
580
580
|
toQuery(): string {
|
|
581
581
|
return this.knexQuery.toQuery();
|
|
582
582
|
}
|
|
583
583
|
|
|
584
|
-
|
|
584
|
+
// 쿼리 디버깅 로그 출력
|
|
585
|
+
debug(): this {
|
|
585
586
|
console.log(
|
|
586
|
-
`${chalk.cyan("[Puri Debug]")} ${chalk.yellow(this.
|
|
587
|
+
`${chalk.cyan("[Puri Debug]")} ${chalk.yellow(this.toQuery())}`
|
|
587
588
|
);
|
|
588
589
|
return this;
|
|
589
590
|
}
|
|
@@ -699,174 +700,62 @@ export class Puri<
|
|
|
699
700
|
return indentedLines.join("\n").trim();
|
|
700
701
|
}
|
|
701
702
|
|
|
703
|
+
// Knex 쿼리 빌더 직접 접근
|
|
702
704
|
raw(): Knex.QueryBuilder {
|
|
703
705
|
return this.knexQuery;
|
|
704
706
|
}
|
|
705
|
-
|
|
706
|
-
increment<
|
|
707
|
-
TColumn extends AvailableColumns<TSchema, TTable, TOriginal, TJoined>,
|
|
708
|
-
>(
|
|
709
|
-
column: TColumn,
|
|
710
|
-
value: number
|
|
711
|
-
): Puri<TSchema, TTable, TOriginal, TResult, TJoined> {
|
|
712
|
-
if (value <= 0) {
|
|
713
|
-
throw new Error("Increment value must be greater than 0");
|
|
714
|
-
}
|
|
715
|
-
this.knexQuery.increment(column, value);
|
|
716
|
-
return this;
|
|
717
|
-
}
|
|
718
|
-
|
|
719
|
-
decrement<
|
|
720
|
-
TColumn extends AvailableColumns<TSchema, TTable, TOriginal, TJoined>,
|
|
721
|
-
>(
|
|
722
|
-
column: TColumn,
|
|
723
|
-
value: number
|
|
724
|
-
): Puri<TSchema, TTable, TOriginal, TResult, TJoined> {
|
|
725
|
-
if (value <= 0) {
|
|
726
|
-
throw new Error("Decrement value must be greater than 0");
|
|
727
|
-
}
|
|
728
|
-
this.knexQuery.decrement(column, value);
|
|
729
|
-
return this;
|
|
730
|
-
}
|
|
731
707
|
}
|
|
732
708
|
|
|
733
|
-
|
|
734
|
-
class WhereGroup<
|
|
735
|
-
TSchema,
|
|
736
|
-
TTable extends keyof TSchema | string,
|
|
737
|
-
TOriginal = any,
|
|
738
|
-
TJoined = EmptyRecord,
|
|
739
|
-
> {
|
|
709
|
+
export class WhereGroup<TTables extends Record<string, any>> {
|
|
740
710
|
constructor(private builder: Knex.QueryBuilder) {}
|
|
741
711
|
|
|
742
|
-
where
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
where<TColumn extends AvailableColumns<TSchema, TTable, TOriginal, TJoined>>(
|
|
712
|
+
// where 메서드들
|
|
713
|
+
where(conditions: WhereCondition<TTables>): this;
|
|
714
|
+
where<TColumn extends AvailableColumns<TTables>>(
|
|
746
715
|
column: TColumn,
|
|
747
|
-
value: ExtractColumnType<
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
TColumn & string,
|
|
751
|
-
TOriginal,
|
|
752
|
-
TJoined
|
|
753
|
-
>
|
|
754
|
-
): WhereGroup<TSchema, TTable, TOriginal, TJoined>;
|
|
755
|
-
where<TColumn extends AvailableColumns<TSchema, TTable, TOriginal, TJoined>>(
|
|
716
|
+
value: ExtractColumnType<TTables, TColumn & string>
|
|
717
|
+
): this;
|
|
718
|
+
where<TColumn extends AvailableColumns<TTables>>(
|
|
756
719
|
column: TColumn,
|
|
757
|
-
operator: ComparisonOperator
|
|
758
|
-
value: ExtractColumnType<
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
TColumn & string,
|
|
762
|
-
TOriginal,
|
|
763
|
-
TJoined
|
|
764
|
-
>
|
|
765
|
-
): WhereGroup<TSchema, TTable, TOriginal, TJoined>;
|
|
766
|
-
where(raw: string): WhereGroup<TSchema, TTable, TOriginal, TJoined>;
|
|
767
|
-
where(...args: any[]): WhereGroup<TSchema, TTable, TOriginal, TJoined> {
|
|
720
|
+
operator: ComparisonOperator,
|
|
721
|
+
value: ExtractColumnType<TTables, TColumn & string>
|
|
722
|
+
): this;
|
|
723
|
+
where(...args: any[]): WhereGroup<TTables> {
|
|
768
724
|
this.builder.where(args[0], ...args.slice(1));
|
|
769
725
|
return this;
|
|
770
726
|
}
|
|
771
727
|
|
|
772
|
-
orWhere
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
orWhere<
|
|
776
|
-
TColumn extends AvailableColumns<TSchema, TTable, TOriginal, TJoined>,
|
|
777
|
-
>(
|
|
728
|
+
// orWhere 메서드들
|
|
729
|
+
orWhere(conditions: WhereCondition<TTables>): this;
|
|
730
|
+
orWhere<TColumn extends AvailableColumns<TTables>>(
|
|
778
731
|
column: TColumn,
|
|
779
|
-
value: ExtractColumnType<
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
TColumn & string,
|
|
783
|
-
TOriginal,
|
|
784
|
-
TJoined
|
|
785
|
-
>
|
|
786
|
-
): WhereGroup<TSchema, TTable, TOriginal, TJoined>;
|
|
787
|
-
orWhere<
|
|
788
|
-
TColumn extends AvailableColumns<TSchema, TTable, TOriginal, TJoined>,
|
|
789
|
-
>(
|
|
732
|
+
value: ExtractColumnType<TTables, TColumn & string>
|
|
733
|
+
): this;
|
|
734
|
+
orWhere<TColumn extends AvailableColumns<TTables>>(
|
|
790
735
|
column: TColumn,
|
|
791
|
-
operator: ComparisonOperator
|
|
792
|
-
value: ExtractColumnType<
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
TColumn & string,
|
|
796
|
-
TOriginal,
|
|
797
|
-
TJoined
|
|
798
|
-
>
|
|
799
|
-
): WhereGroup<TSchema, TTable, TOriginal, TJoined>;
|
|
800
|
-
orWhere(raw: string): WhereGroup<TSchema, TTable, TOriginal, TJoined>;
|
|
801
|
-
orWhere(...args: any[]): WhereGroup<TSchema, TTable, TOriginal, TJoined> {
|
|
736
|
+
operator: ComparisonOperator,
|
|
737
|
+
value: ExtractColumnType<TTables, TColumn & string>
|
|
738
|
+
): this;
|
|
739
|
+
orWhere(...args: any[]): WhereGroup<TTables> {
|
|
802
740
|
this.builder.orWhere(args[0], ...args.slice(1));
|
|
803
741
|
return this;
|
|
804
742
|
}
|
|
805
743
|
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
>
|
|
809
|
-
column: TColumn,
|
|
810
|
-
values: ExtractColumnType<
|
|
811
|
-
TSchema,
|
|
812
|
-
TTable,
|
|
813
|
-
TColumn & string,
|
|
814
|
-
TOriginal,
|
|
815
|
-
TJoined
|
|
816
|
-
>[]
|
|
817
|
-
): WhereGroup<TSchema, TTable, TOriginal, TJoined>;
|
|
818
|
-
whereIn(
|
|
819
|
-
column: string,
|
|
820
|
-
values: any[]
|
|
821
|
-
): WhereGroup<TSchema, TTable, TOriginal, TJoined> {
|
|
822
|
-
this.builder.whereIn(column, values);
|
|
823
|
-
return this;
|
|
824
|
-
}
|
|
825
|
-
|
|
826
|
-
orWhereIn<
|
|
827
|
-
TColumn extends AvailableColumns<TSchema, TTable, TOriginal, TJoined>,
|
|
828
|
-
>(
|
|
829
|
-
column: TColumn,
|
|
830
|
-
values: ExtractColumnType<
|
|
831
|
-
TSchema,
|
|
832
|
-
TTable,
|
|
833
|
-
TColumn & string,
|
|
834
|
-
TOriginal,
|
|
835
|
-
TJoined
|
|
836
|
-
>[]
|
|
837
|
-
): WhereGroup<TSchema, TTable, TOriginal, TJoined>;
|
|
838
|
-
orWhereIn(
|
|
839
|
-
column: string,
|
|
840
|
-
values: any[]
|
|
841
|
-
): WhereGroup<TSchema, TTable, TOriginal, TJoined> {
|
|
842
|
-
this.builder.orWhereIn(column, values);
|
|
843
|
-
return this;
|
|
844
|
-
}
|
|
845
|
-
|
|
846
|
-
// 중첩 그룹 지원
|
|
847
|
-
whereGroup(
|
|
848
|
-
callback: (
|
|
849
|
-
group: WhereGroup<TSchema, TTable, TOriginal, TJoined>
|
|
850
|
-
) => WhereGroup<TSchema, TTable, TOriginal, TJoined>
|
|
851
|
-
): WhereGroup<TSchema, TTable, TOriginal, TJoined> {
|
|
744
|
+
// 중첩 그룹
|
|
745
|
+
whereGroup(callback: (g: WhereGroup<TTables>) => void): this;
|
|
746
|
+
whereGroup(callback: (g: WhereGroup<TTables>) => void): WhereGroup<TTables> {
|
|
852
747
|
this.builder.where((subBuilder) => {
|
|
853
|
-
const subGroup = new WhereGroup<
|
|
854
|
-
subBuilder
|
|
855
|
-
);
|
|
748
|
+
const subGroup = new WhereGroup<TTables>(subBuilder);
|
|
856
749
|
callback(subGroup);
|
|
857
750
|
});
|
|
858
751
|
return this;
|
|
859
752
|
}
|
|
860
|
-
|
|
753
|
+
orWhereGroup(callback: (g: WhereGroup<TTables>) => void): this;
|
|
861
754
|
orWhereGroup(
|
|
862
|
-
callback: (
|
|
863
|
-
|
|
864
|
-
) => WhereGroup<TSchema, TTable, TOriginal, TJoined>
|
|
865
|
-
): WhereGroup<TSchema, TTable, TOriginal, TJoined> {
|
|
755
|
+
callback: (g: WhereGroup<TTables>) => void
|
|
756
|
+
): WhereGroup<TTables> {
|
|
866
757
|
this.builder.orWhere((subBuilder) => {
|
|
867
|
-
const subGroup = new WhereGroup<
|
|
868
|
-
subBuilder
|
|
869
|
-
);
|
|
758
|
+
const subGroup = new WhereGroup<TTables>(subBuilder);
|
|
870
759
|
callback(subGroup);
|
|
871
760
|
});
|
|
872
761
|
return this;
|
|
@@ -874,37 +763,39 @@ class WhereGroup<
|
|
|
874
763
|
}
|
|
875
764
|
|
|
876
765
|
export class JoinClauseGroup<
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
TOriginal = any,
|
|
880
|
-
TJoined = EmptyRecord,
|
|
766
|
+
TLeft extends Record<string, any>,
|
|
767
|
+
TRight extends Record<string, any>,
|
|
881
768
|
> {
|
|
882
769
|
constructor(private callback: Knex.JoinClause) {}
|
|
883
770
|
|
|
771
|
+
// ON(AND): 컬럼 = 컬럼
|
|
772
|
+
on(left: AvailableColumns<TLeft>, right: AvailableColumns<TRight>): this;
|
|
773
|
+
// ON(AND): 컬럼 (연산자) 컬럼
|
|
884
774
|
on(
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
):
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
):
|
|
893
|
-
on(...args: any[]): JoinClauseGroup<TSchema, TTable, TOriginal, TJoined> {
|
|
775
|
+
left: AvailableColumns<TLeft>,
|
|
776
|
+
operator: ComparisonOperator,
|
|
777
|
+
right: AvailableColumns<TRight>
|
|
778
|
+
): this;
|
|
779
|
+
// ON(AND): 콜백
|
|
780
|
+
on(callback: (nested: JoinClauseGroup<TLeft, TRight>) => void): this;
|
|
781
|
+
// ON(AND) 구현
|
|
782
|
+
on(...args: any[]): this {
|
|
894
783
|
this.callback.on(...(args as [string, string]));
|
|
895
784
|
return this;
|
|
896
785
|
}
|
|
897
786
|
|
|
787
|
+
// ON(OR): 컬럼 = 컬럼
|
|
788
|
+
orOn(left: AvailableColumns<TLeft>, right: AvailableColumns<TRight>): this;
|
|
789
|
+
// ON(OR): 컬럼 (연산자) 컬럼
|
|
898
790
|
orOn(
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
):
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
):
|
|
907
|
-
orOn(...args: any[]): JoinClauseGroup<TSchema, TTable, TOriginal, TJoined> {
|
|
791
|
+
left: AvailableColumns<TLeft>,
|
|
792
|
+
operator: ComparisonOperator,
|
|
793
|
+
right: AvailableColumns<TRight>
|
|
794
|
+
): this;
|
|
795
|
+
// ON(OR): 콜백
|
|
796
|
+
orOn(callback: (nested: JoinClauseGroup<TLeft, TRight>) => void): this;
|
|
797
|
+
// ON(OR) 구현
|
|
798
|
+
orOn(...args: any[]): this {
|
|
908
799
|
this.callback.orOn(...(args as [string, string]));
|
|
909
800
|
return this;
|
|
910
801
|
}
|