@zuzjs/orm 0.3.3 → 0.3.5
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/drivers/expressionBuilder.d.ts +21 -8
- package/dist/drivers/expressionBuilder.js +79 -25
- package/dist/drivers/mysql/index.js +80 -0
- package/dist/drivers/queryBuilder.d.ts +3 -4
- package/dist/drivers/queryBuilder.js +19 -9
- package/dist/types.d.ts +1 -1
- package/dist/types.js +4 -1
- package/package.json +1 -1
|
@@ -1,19 +1,32 @@
|
|
|
1
1
|
import { ObjectLiteral } from "typeorm";
|
|
2
2
|
declare class ZormExprBuilder<T extends ObjectLiteral> {
|
|
3
|
-
private
|
|
4
|
-
private
|
|
5
|
-
private
|
|
6
|
-
|
|
3
|
+
private _parts;
|
|
4
|
+
private _params;
|
|
5
|
+
private _paramIdx;
|
|
6
|
+
private _alias;
|
|
7
|
+
private static globalParamIdx;
|
|
8
|
+
constructor(alias?: string, parent?: {
|
|
9
|
+
params: Record<string, any>;
|
|
10
|
+
idx: number;
|
|
11
|
+
});
|
|
12
|
+
field(col: keyof T | string): this;
|
|
13
|
+
equals(value: any): this;
|
|
14
|
+
append(extra: string): this;
|
|
15
|
+
wrap(wrapper: (expr: string) => string): this;
|
|
16
|
+
exists(sub: (q: ZormExprBuilder<T>) => ZormExprBuilder<T>): this;
|
|
17
|
+
select(expr: string): this;
|
|
18
|
+
from(table: string, alias: string): this;
|
|
19
|
+
where(cond: Record<string, any>): this;
|
|
20
|
+
or(): this;
|
|
21
|
+
group(): this;
|
|
7
22
|
fromUnixTime(): this;
|
|
8
23
|
date(): this;
|
|
9
24
|
substring(column: keyof T | string, delimiter: string, index: number): this;
|
|
10
|
-
|
|
11
|
-
wrap(wrapper: (expr: string) => string): this;
|
|
12
|
-
equals(value: string | number | boolean): {
|
|
25
|
+
toExpression(): {
|
|
13
26
|
expression: string;
|
|
14
27
|
param: Record<string, any>;
|
|
15
28
|
};
|
|
16
29
|
buildExpression(): string;
|
|
17
|
-
|
|
30
|
+
buildParams(): Record<string, any>;
|
|
18
31
|
}
|
|
19
32
|
export default ZormExprBuilder;
|
|
@@ -1,46 +1,100 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
class ZormExprBuilder {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
4
|
+
_parts = [];
|
|
5
|
+
_params = {};
|
|
6
|
+
_paramIdx = 0;
|
|
7
|
+
_alias;
|
|
8
|
+
static globalParamIdx = 0;
|
|
9
|
+
constructor(alias, parent) {
|
|
10
|
+
this._alias = alias ? `${alias}.` : ``;
|
|
11
|
+
this._paramIdx = parent ? parent.idx : 0;
|
|
12
|
+
this._params = parent ? parent.params : {};
|
|
13
|
+
}
|
|
14
|
+
field(col) {
|
|
15
|
+
this._parts.push(`${this._alias}${String(col)}`);
|
|
9
16
|
return this;
|
|
10
17
|
}
|
|
11
|
-
|
|
12
|
-
|
|
18
|
+
equals(value) {
|
|
19
|
+
const key = `p${this._paramIdx++}`;
|
|
20
|
+
this.append(` = :${key}`);
|
|
21
|
+
// this._parts[this._parts.length - 1] += ` = :${key}`;
|
|
22
|
+
this._params[key] = value;
|
|
13
23
|
return this;
|
|
14
24
|
}
|
|
15
|
-
|
|
16
|
-
this.
|
|
25
|
+
append(extra) {
|
|
26
|
+
if (this._parts.length === 0)
|
|
27
|
+
throw new Error("Cannot append to empty expression");
|
|
28
|
+
this._parts[this._parts.length - 1] += extra;
|
|
17
29
|
return this;
|
|
18
30
|
}
|
|
19
|
-
|
|
20
|
-
this.
|
|
31
|
+
wrap(wrapper) {
|
|
32
|
+
if (this._parts.length === 0)
|
|
33
|
+
throw new Error("Cannot wrap empty expression");
|
|
34
|
+
this._parts[this._parts.length - 1] = wrapper(this._parts[this._parts.length - 1]);
|
|
21
35
|
return this;
|
|
22
36
|
}
|
|
23
|
-
|
|
24
|
-
|
|
37
|
+
exists(sub) {
|
|
38
|
+
const subQ = new ZormExprBuilder(undefined, { params: this._params, idx: this._paramIdx });
|
|
39
|
+
sub(subQ);
|
|
40
|
+
// const { expression, param } = subQ.toExpression();
|
|
41
|
+
this._parts.push(`EXISTS (${subQ.buildExpression()})`);
|
|
42
|
+
// Object.assign(this._params, param);
|
|
43
|
+
this._paramIdx = subQ._paramIdx;
|
|
25
44
|
return this;
|
|
26
45
|
}
|
|
27
|
-
|
|
28
|
-
this.
|
|
46
|
+
select(expr) {
|
|
47
|
+
this._parts.push(`SELECT ${expr}`);
|
|
29
48
|
return this;
|
|
30
49
|
}
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
this.
|
|
34
|
-
return
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
50
|
+
from(table, alias) {
|
|
51
|
+
this._parts.push(`FROM ${table} ${alias}`);
|
|
52
|
+
this._alias = alias;
|
|
53
|
+
return this;
|
|
54
|
+
}
|
|
55
|
+
where(cond) {
|
|
56
|
+
const whereParts = [];
|
|
57
|
+
for (const [k, v] of Object.entries(cond)) {
|
|
58
|
+
const key = `p${this._paramIdx++}`;
|
|
59
|
+
whereParts.push(`${k} = :${key}`);
|
|
60
|
+
this._params[key] = v;
|
|
61
|
+
}
|
|
62
|
+
this._parts.push(`WHERE ${whereParts.join(' AND ')}`);
|
|
63
|
+
return this;
|
|
64
|
+
}
|
|
65
|
+
// or(sub: ZormExprBuilder<any> | { expression: string; param: Record<string, any> }): this {
|
|
66
|
+
// const { expression, param } = 'toExpression' in sub ? sub.toExpression() : sub;
|
|
67
|
+
// this._parts.push(this._parts.length === 0 ? expression : `OR ${expression}`);
|
|
68
|
+
// Object.assign(this._params, param);
|
|
69
|
+
// return this;
|
|
70
|
+
// }
|
|
71
|
+
or() {
|
|
72
|
+
this._parts.push(`OR`);
|
|
73
|
+
return this;
|
|
74
|
+
}
|
|
75
|
+
group() {
|
|
76
|
+
return this.wrap(e => `(${e})`);
|
|
77
|
+
}
|
|
78
|
+
fromUnixTime() {
|
|
79
|
+
this.wrap(expr => `FROM_UNIXTIME(${expr})`);
|
|
80
|
+
return this;
|
|
81
|
+
}
|
|
82
|
+
date() {
|
|
83
|
+
this.wrap(expr => `DATE(${expr})`);
|
|
84
|
+
return this;
|
|
85
|
+
}
|
|
86
|
+
substring(column, delimiter, index) {
|
|
87
|
+
this._parts = [`SUBSTRING_INDEX(${this._alias}${String(column)}, '${delimiter}', ${index})`];
|
|
88
|
+
return this;
|
|
89
|
+
}
|
|
90
|
+
toExpression() {
|
|
91
|
+
return { expression: this._parts.join(' '), param: this._params };
|
|
38
92
|
}
|
|
39
93
|
buildExpression() {
|
|
40
|
-
return this.
|
|
94
|
+
return this.toExpression().expression;
|
|
41
95
|
}
|
|
42
|
-
|
|
43
|
-
return this.
|
|
96
|
+
buildParams() {
|
|
97
|
+
return this.toExpression().param;
|
|
44
98
|
}
|
|
45
99
|
}
|
|
46
100
|
exports.default = ZormExprBuilder;
|
|
@@ -156,6 +156,49 @@ class MySqlDriver {
|
|
|
156
156
|
WHERE TABLE_SCHEMA = ? AND TABLE_NAME = ? AND REFERENCED_TABLE_NAME IS NOT NULL`, [this.conn.database, tableName]);
|
|
157
157
|
foreignKeys[tableName] = fkResults;
|
|
158
158
|
}
|
|
159
|
+
// Track inverse relations: { tableName: { fkColumn: targetTable } }
|
|
160
|
+
const inverseRelations = {};
|
|
161
|
+
// Populate inverse map
|
|
162
|
+
for (const [tableName, fks] of Object.entries(foreignKeys)) {
|
|
163
|
+
for (const fk of fks) {
|
|
164
|
+
const targetTable = fk.REFERENCED_TABLE_NAME;
|
|
165
|
+
const fkColumn = fk.COLUMN_NAME;
|
|
166
|
+
if (!inverseRelations[targetTable]) {
|
|
167
|
+
inverseRelations[targetTable] = [];
|
|
168
|
+
}
|
|
169
|
+
inverseRelations[targetTable].push({
|
|
170
|
+
fkColumn,
|
|
171
|
+
targetTable: tableName,
|
|
172
|
+
targetEntity: (0, index_js_1.toPascalCase)(tableName)
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
// Detect 2 Column Junction Tables for ManyToMany
|
|
177
|
+
const junctionTables = {};
|
|
178
|
+
for (const tableName of tableNames) {
|
|
179
|
+
const [cols] = await this.pool.execute(`SELECT COLUMN_NAME, COLUMN_KEY, EXTRA FROM INFORMATION_SCHEMA.COLUMNS
|
|
180
|
+
WHERE TABLE_SCHEMA = ? AND TABLE_NAME = ?`, [this.conn.database, tableName]);
|
|
181
|
+
const fks = foreignKeys[String(tableName)] || [];
|
|
182
|
+
if (fks.length !== 2)
|
|
183
|
+
continue;
|
|
184
|
+
// Must have exactly 2 columns
|
|
185
|
+
if (cols.length !== 2)
|
|
186
|
+
continue;
|
|
187
|
+
// Both columns must be primary key
|
|
188
|
+
if (cols.filter(c => c.COLUMN_KEY !== 'PRI').length > 0)
|
|
189
|
+
continue;
|
|
190
|
+
const [left, right] = fks;
|
|
191
|
+
const [t1, t2] = [left.REFERENCED_TABLE_NAME, right.REFERENCED_TABLE_NAME].sort();
|
|
192
|
+
const key = `${t1}_${t2}`;
|
|
193
|
+
if (!junctionTables[key]) {
|
|
194
|
+
junctionTables[key] = {
|
|
195
|
+
left: t1,
|
|
196
|
+
right: t2,
|
|
197
|
+
leftCol: left.COLUMN_NAME,
|
|
198
|
+
rightCol: right.COLUMN_NAME
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
}
|
|
159
202
|
for (const tableName of tableNames) {
|
|
160
203
|
const enums = [];
|
|
161
204
|
const imports = [];
|
|
@@ -222,6 +265,43 @@ class MySqlDriver {
|
|
|
222
265
|
}
|
|
223
266
|
}
|
|
224
267
|
}
|
|
268
|
+
// Add OneToMany Relations
|
|
269
|
+
const inverse = inverseRelations[String(tableName)] || [];
|
|
270
|
+
for (const rel of inverse) {
|
|
271
|
+
const propName = rel.targetTable.endsWith('s') ? rel.targetTable : `${rel.targetTable}s`;
|
|
272
|
+
// Avoid duplicate
|
|
273
|
+
if (entityCode.some(line => line.includes(`@${propName}`)))
|
|
274
|
+
continue;
|
|
275
|
+
const importLine = `import { ${rel.targetEntity} } from "./${rel.targetTable}";`;
|
|
276
|
+
if (!imports.includes(importLine)) {
|
|
277
|
+
imports.push(importLine);
|
|
278
|
+
}
|
|
279
|
+
if (!_imports.includes('OneToMany'))
|
|
280
|
+
_imports.push('OneToMany');
|
|
281
|
+
entityCode.push(`\t@OneToMany(() => ${rel.targetEntity}, r => r.${rel.fkColumn})`);
|
|
282
|
+
entityCode.push(`\tfk${(0, index_js_1.toPascalCase)(propName)}!: ${rel.targetEntity}[];\n`);
|
|
283
|
+
}
|
|
284
|
+
// Add Many-to-Many Relations
|
|
285
|
+
const junctions = Object.values(junctionTables)
|
|
286
|
+
.filter(j => j.left === tableName || j.right === tableName);
|
|
287
|
+
for (const j of junctions) {
|
|
288
|
+
const targetTable = j.left === tableName ? j.right : j.left;
|
|
289
|
+
const targetEntity = (0, index_js_1.toPascalCase)(targetTable);
|
|
290
|
+
const propName = targetTable.endsWith('s') ? targetTable : `${targetTable}s`;
|
|
291
|
+
if (entityCode.some(line => line.includes(`@${propName}`)))
|
|
292
|
+
continue;
|
|
293
|
+
const importLine = `import { ${targetEntity} } from "./${targetTable}";`;
|
|
294
|
+
if (!imports.includes(importLine)) {
|
|
295
|
+
imports.push(importLine);
|
|
296
|
+
}
|
|
297
|
+
if (!_imports.includes('ManyToMany'))
|
|
298
|
+
_imports.push('ManyToMany');
|
|
299
|
+
if (!_imports.includes('JoinTable'))
|
|
300
|
+
_imports.push('JoinTable');
|
|
301
|
+
entityCode.push(`\t@ManyToMany(() => ${targetEntity})`);
|
|
302
|
+
entityCode.push(`\t@JoinTable()`);
|
|
303
|
+
entityCode.push(`\tom${propName}!: ${targetEntity}[];\n`);
|
|
304
|
+
}
|
|
225
305
|
const Code = [
|
|
226
306
|
`/**`,
|
|
227
307
|
`* AutoGenerated by @zuzjs/orm.`,
|
|
@@ -45,10 +45,9 @@ declare class ZormQueryBuilder<T extends ObjectLiteral, R = QueryResult> extends
|
|
|
45
45
|
* Adds a custom expression-based WHERE clause using a fluent builder.
|
|
46
46
|
* @param fn - A callback that receives a ZormExprBuilder and returns an expression + param.
|
|
47
47
|
*/
|
|
48
|
-
expression(exprFn: (q: ZormExprBuilder<T>) =>
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
} | ZormExprBuilder<T>): this;
|
|
48
|
+
expression(exprFn: (q: ZormExprBuilder<T>) => ZormExprBuilder<T>,
|
|
49
|
+
/** Add parentheses group to built expression */
|
|
50
|
+
group?: boolean): this;
|
|
52
51
|
/**
|
|
53
52
|
* Adds a WHERE condition to the query.
|
|
54
53
|
* @param condition - The condition to be added.
|
|
@@ -179,19 +179,29 @@ class ZormQueryBuilder extends Promise {
|
|
|
179
179
|
* Adds a custom expression-based WHERE clause using a fluent builder.
|
|
180
180
|
* @param fn - A callback that receives a ZormExprBuilder and returns an expression + param.
|
|
181
181
|
*/
|
|
182
|
-
expression(exprFn
|
|
182
|
+
expression(exprFn,
|
|
183
|
+
/** Add parentheses group to built expression */
|
|
184
|
+
group = true) {
|
|
183
185
|
const qb = this.queryBuilder;
|
|
184
|
-
const result = exprFn(new expressionBuilder_1.default());
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
}
|
|
188
|
-
else {
|
|
189
|
-
// fallback if only expression was built without .equals()
|
|
190
|
-
qb.andWhere(result.buildExpression(), result.buildParam());
|
|
191
|
-
}
|
|
186
|
+
const result = exprFn(new expressionBuilder_1.default(this.entityAlias));
|
|
187
|
+
const _expression = result.buildExpression();
|
|
188
|
+
qb.andWhere(group ? `(${_expression})` : _expression, result.buildParams());
|
|
192
189
|
this.whereCount++;
|
|
193
190
|
return this;
|
|
194
191
|
}
|
|
192
|
+
// expression(exprFn: (q: ZormExprBuilder<T>) => ZormExprBuilder<T>): this {
|
|
193
|
+
// const qb = this.queryBuilder as SelectQueryBuilder<T> | UpdateQueryBuilder<T>;
|
|
194
|
+
// const result = exprFn(new ZormExprBuilder<T>(this.entityAlias));
|
|
195
|
+
// // const expression =
|
|
196
|
+
// qb.andWhere(result.buildExpression(), result.buildParams());
|
|
197
|
+
// // if ('expression' in result && 'param' in result) {
|
|
198
|
+
// // } else {
|
|
199
|
+
// // // fallback if only expression was built without .equals()
|
|
200
|
+
// // }
|
|
201
|
+
// // qb.andWhere(result.buildExpression(), result.buildParams());
|
|
202
|
+
// this.whereCount++;
|
|
203
|
+
// return this;
|
|
204
|
+
// }
|
|
195
205
|
/**
|
|
196
206
|
* Adds a WHERE condition to the query.
|
|
197
207
|
* @param condition - The condition to be added.
|
package/dist/types.d.ts
CHANGED
|
@@ -101,4 +101,4 @@ export type DeleteQueryResult = {
|
|
|
101
101
|
count: number;
|
|
102
102
|
error?: QueryError;
|
|
103
103
|
};
|
|
104
|
-
export { BaseEntity, Column, Entity, JoinColumn, OneToOne, PrimaryColumn, PrimaryGeneratedColumn } from "typeorm";
|
|
104
|
+
export { BaseEntity, Column, Entity, JoinColumn, ManyToMany, ManyToOne, OneToMany, OneToOne, PrimaryColumn, PrimaryGeneratedColumn } from "typeorm";
|
package/dist/types.js
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.PrimaryGeneratedColumn = exports.PrimaryColumn = exports.OneToOne = exports.JoinColumn = exports.Entity = exports.Column = exports.BaseEntity = void 0;
|
|
3
|
+
exports.PrimaryGeneratedColumn = exports.PrimaryColumn = exports.OneToOne = exports.OneToMany = exports.ManyToOne = exports.ManyToMany = exports.JoinColumn = exports.Entity = exports.Column = exports.BaseEntity = void 0;
|
|
4
4
|
var typeorm_1 = require("typeorm");
|
|
5
5
|
Object.defineProperty(exports, "BaseEntity", { enumerable: true, get: function () { return typeorm_1.BaseEntity; } });
|
|
6
6
|
Object.defineProperty(exports, "Column", { enumerable: true, get: function () { return typeorm_1.Column; } });
|
|
7
7
|
Object.defineProperty(exports, "Entity", { enumerable: true, get: function () { return typeorm_1.Entity; } });
|
|
8
8
|
Object.defineProperty(exports, "JoinColumn", { enumerable: true, get: function () { return typeorm_1.JoinColumn; } });
|
|
9
|
+
Object.defineProperty(exports, "ManyToMany", { enumerable: true, get: function () { return typeorm_1.ManyToMany; } });
|
|
10
|
+
Object.defineProperty(exports, "ManyToOne", { enumerable: true, get: function () { return typeorm_1.ManyToOne; } });
|
|
11
|
+
Object.defineProperty(exports, "OneToMany", { enumerable: true, get: function () { return typeorm_1.OneToMany; } });
|
|
9
12
|
Object.defineProperty(exports, "OneToOne", { enumerable: true, get: function () { return typeorm_1.OneToOne; } });
|
|
10
13
|
Object.defineProperty(exports, "PrimaryColumn", { enumerable: true, get: function () { return typeorm_1.PrimaryColumn; } });
|
|
11
14
|
Object.defineProperty(exports, "PrimaryGeneratedColumn", { enumerable: true, get: function () { return typeorm_1.PrimaryGeneratedColumn; } });
|