@itrocks/mysql 0.1.4 → 0.1.6
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/cjs/mysql.d.ts +2 -0
- package/cjs/mysql.js +26 -13
- package/esm/mysql.d.ts +2 -0
- package/esm/mysql.js +26 -13
- package/package.json +1 -1
- package/src/mysql.d.ts +69 -0
- package/src/mysql.js +404 -0
package/cjs/mysql.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { AnyObject } from '@itrocks/class-type';
|
|
|
2
2
|
import { KeyOf } from '@itrocks/class-type';
|
|
3
3
|
import { ObjectOrType } from '@itrocks/class-type';
|
|
4
4
|
import { Type } from '@itrocks/class-type';
|
|
5
|
+
import { ReflectProperty } from '@itrocks/reflect';
|
|
5
6
|
import { DataSource } from '@itrocks/storage';
|
|
6
7
|
import { Entity } from '@itrocks/storage';
|
|
7
8
|
import { MayEntity } from '@itrocks/storage';
|
|
@@ -37,6 +38,7 @@ export declare class Mysql extends DataSource {
|
|
|
37
38
|
password: string;
|
|
38
39
|
database: string;
|
|
39
40
|
});
|
|
41
|
+
columnName<T extends object>(property: ReflectProperty<T>): string | undefined;
|
|
40
42
|
connect(): Promise<Connection>;
|
|
41
43
|
count<T extends object>(type: Type<T>, search?: SearchType<T>): Promise<number>;
|
|
42
44
|
delete<T extends object>(object: Entity<T>, property?: KeyOf<Entity<T>>): Promise<T>;
|
package/cjs/mysql.js
CHANGED
|
@@ -47,6 +47,15 @@ class Mysql extends storage_1.DataSource {
|
|
|
47
47
|
super();
|
|
48
48
|
this.config = config;
|
|
49
49
|
}
|
|
50
|
+
columnName(property) {
|
|
51
|
+
const propertyType = property.type;
|
|
52
|
+
if (propertyType instanceof property_type_1.CollectionType)
|
|
53
|
+
return;
|
|
54
|
+
const propertyName = ((0, class_type_3.isAnyType)(propertyType.type) && depends.storeOf(propertyType.type))
|
|
55
|
+
? property.name + 'Id'
|
|
56
|
+
: property.name;
|
|
57
|
+
return depends.columnOf(propertyName);
|
|
58
|
+
}
|
|
50
59
|
async connect() {
|
|
51
60
|
const mariaDbConfig = Object.assign(this.config, {
|
|
52
61
|
allowPublicKeyRetrieval: true,
|
|
@@ -56,9 +65,10 @@ class Mysql extends storage_1.DataSource {
|
|
|
56
65
|
}
|
|
57
66
|
async count(type, search = {}) {
|
|
58
67
|
const connection = this.connection ?? await this.connect();
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
const
|
|
68
|
+
const localSearch = { ...search };
|
|
69
|
+
Object.setPrototypeOf(localSearch, type.prototype);
|
|
70
|
+
const sql = this.propertiesToSearchSql(localSearch);
|
|
71
|
+
const [values] = await this.valuesToDb(localSearch);
|
|
62
72
|
if (exports.DEBUG)
|
|
63
73
|
console.log('SELECT COUNT(*) FROM `' + depends.storeOf(type) + '`' + sql, JSON.stringify(values));
|
|
64
74
|
const row = (await connection.query('SELECT COUNT(*) `count` FROM `' + depends.storeOf(type) + '`' + sql, Object.values(values)))[0];
|
|
@@ -144,13 +154,10 @@ class Mysql extends storage_1.DataSource {
|
|
|
144
154
|
propertiesToSqlSelect(type) {
|
|
145
155
|
const sql = ['id'];
|
|
146
156
|
for (const property of new reflect_1.ReflectClass(type).properties) {
|
|
147
|
-
const
|
|
148
|
-
if (
|
|
157
|
+
const columnName = this.columnName(property);
|
|
158
|
+
if (!columnName)
|
|
149
159
|
continue;
|
|
150
|
-
const propertyName =
|
|
151
|
-
? property.name + 'Id'
|
|
152
|
-
: property.name;
|
|
153
|
-
const columnName = depends.columnOf(propertyName);
|
|
160
|
+
const propertyName = property.name;
|
|
154
161
|
sql.push((columnName.length !== propertyName.length)
|
|
155
162
|
? ('`' + columnName + '` `' + propertyName + '`')
|
|
156
163
|
: ('`' + propertyName + '`'));
|
|
@@ -334,9 +341,10 @@ class Mysql extends storage_1.DataSource {
|
|
|
334
341
|
sortOption = option.properties.length ? option : new storage_3.Sort((0, sort_2.sortOf)(type));
|
|
335
342
|
}
|
|
336
343
|
}
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
const
|
|
344
|
+
const localSearch = { ...search };
|
|
345
|
+
Object.setPrototypeOf(localSearch, type.prototype);
|
|
346
|
+
const sql = this.propertiesToSearchSql(localSearch);
|
|
347
|
+
const [values] = await this.valuesToDb(localSearch);
|
|
340
348
|
if (exports.DEBUG)
|
|
341
349
|
console.log('SELECT ' + propertiesSql + ' FROM `' + depends.storeOf(type) + '`' + sql, JSON.stringify(values));
|
|
342
350
|
const limit = limitOption?.limit ? ' LIMIT ' + limitOption.limit : '';
|
|
@@ -344,7 +352,12 @@ class Mysql extends storage_1.DataSource {
|
|
|
344
352
|
const sort = sortOption?.properties.length
|
|
345
353
|
? ' ORDER BY '
|
|
346
354
|
+ sortOption.properties
|
|
347
|
-
.map(property =>
|
|
355
|
+
.map(property => ({
|
|
356
|
+
column: this.columnName(new reflect_2.ReflectProperty(type, '' + property)),
|
|
357
|
+
reverse: property instanceof sort_1.Reverse
|
|
358
|
+
}))
|
|
359
|
+
.filter(property => property.column)
|
|
360
|
+
.map(property => '`' + property.column + '`' + (property.reverse ? ' DESC' : ''))
|
|
348
361
|
.join(', ')
|
|
349
362
|
: '';
|
|
350
363
|
const rows = await connection.query('SELECT ' + propertiesSql + ' FROM `' + depends.storeOf(type) + '`' + sql + sort + limit + offset, Object.values(values));
|
package/esm/mysql.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { AnyObject } from '@itrocks/class-type';
|
|
|
2
2
|
import { KeyOf } from '@itrocks/class-type';
|
|
3
3
|
import { ObjectOrType } from '@itrocks/class-type';
|
|
4
4
|
import { Type } from '@itrocks/class-type';
|
|
5
|
+
import { ReflectProperty } from '@itrocks/reflect';
|
|
5
6
|
import { DataSource } from '@itrocks/storage';
|
|
6
7
|
import { Entity } from '@itrocks/storage';
|
|
7
8
|
import { MayEntity } from '@itrocks/storage';
|
|
@@ -37,6 +38,7 @@ export declare class Mysql extends DataSource {
|
|
|
37
38
|
password: string;
|
|
38
39
|
database: string;
|
|
39
40
|
});
|
|
41
|
+
columnName<T extends object>(property: ReflectProperty<T>): string | undefined;
|
|
40
42
|
connect(): Promise<Connection>;
|
|
41
43
|
count<T extends object>(type: Type<T>, search?: SearchType<T>): Promise<number>;
|
|
42
44
|
delete<T extends object>(object: Entity<T>, property?: KeyOf<Entity<T>>): Promise<T>;
|
package/esm/mysql.js
CHANGED
|
@@ -42,6 +42,15 @@ export class Mysql extends DataSource {
|
|
|
42
42
|
super();
|
|
43
43
|
this.config = config;
|
|
44
44
|
}
|
|
45
|
+
columnName(property) {
|
|
46
|
+
const propertyType = property.type;
|
|
47
|
+
if (propertyType instanceof CollectionType)
|
|
48
|
+
return;
|
|
49
|
+
const propertyName = (isAnyType(propertyType.type) && depends.storeOf(propertyType.type))
|
|
50
|
+
? property.name + 'Id'
|
|
51
|
+
: property.name;
|
|
52
|
+
return depends.columnOf(propertyName);
|
|
53
|
+
}
|
|
45
54
|
async connect() {
|
|
46
55
|
const mariaDbConfig = Object.assign(this.config, {
|
|
47
56
|
allowPublicKeyRetrieval: true,
|
|
@@ -51,9 +60,10 @@ export class Mysql extends DataSource {
|
|
|
51
60
|
}
|
|
52
61
|
async count(type, search = {}) {
|
|
53
62
|
const connection = this.connection ?? await this.connect();
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
const
|
|
63
|
+
const localSearch = { ...search };
|
|
64
|
+
Object.setPrototypeOf(localSearch, type.prototype);
|
|
65
|
+
const sql = this.propertiesToSearchSql(localSearch);
|
|
66
|
+
const [values] = await this.valuesToDb(localSearch);
|
|
57
67
|
if (DEBUG)
|
|
58
68
|
console.log('SELECT COUNT(*) FROM `' + depends.storeOf(type) + '`' + sql, JSON.stringify(values));
|
|
59
69
|
const row = (await connection.query('SELECT COUNT(*) `count` FROM `' + depends.storeOf(type) + '`' + sql, Object.values(values)))[0];
|
|
@@ -139,13 +149,10 @@ export class Mysql extends DataSource {
|
|
|
139
149
|
propertiesToSqlSelect(type) {
|
|
140
150
|
const sql = ['id'];
|
|
141
151
|
for (const property of new ReflectClass(type).properties) {
|
|
142
|
-
const
|
|
143
|
-
if (
|
|
152
|
+
const columnName = this.columnName(property);
|
|
153
|
+
if (!columnName)
|
|
144
154
|
continue;
|
|
145
|
-
const propertyName =
|
|
146
|
-
? property.name + 'Id'
|
|
147
|
-
: property.name;
|
|
148
|
-
const columnName = depends.columnOf(propertyName);
|
|
155
|
+
const propertyName = property.name;
|
|
149
156
|
sql.push((columnName.length !== propertyName.length)
|
|
150
157
|
? ('`' + columnName + '` `' + propertyName + '`')
|
|
151
158
|
: ('`' + propertyName + '`'));
|
|
@@ -329,9 +336,10 @@ export class Mysql extends DataSource {
|
|
|
329
336
|
sortOption = option.properties.length ? option : new Sort(sortOf(type));
|
|
330
337
|
}
|
|
331
338
|
}
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
const
|
|
339
|
+
const localSearch = { ...search };
|
|
340
|
+
Object.setPrototypeOf(localSearch, type.prototype);
|
|
341
|
+
const sql = this.propertiesToSearchSql(localSearch);
|
|
342
|
+
const [values] = await this.valuesToDb(localSearch);
|
|
335
343
|
if (DEBUG)
|
|
336
344
|
console.log('SELECT ' + propertiesSql + ' FROM `' + depends.storeOf(type) + '`' + sql, JSON.stringify(values));
|
|
337
345
|
const limit = limitOption?.limit ? ' LIMIT ' + limitOption.limit : '';
|
|
@@ -339,7 +347,12 @@ export class Mysql extends DataSource {
|
|
|
339
347
|
const sort = sortOption?.properties.length
|
|
340
348
|
? ' ORDER BY '
|
|
341
349
|
+ sortOption.properties
|
|
342
|
-
.map(property =>
|
|
350
|
+
.map(property => ({
|
|
351
|
+
column: this.columnName(new ReflectProperty(type, '' + property)),
|
|
352
|
+
reverse: property instanceof Reverse
|
|
353
|
+
}))
|
|
354
|
+
.filter(property => property.column)
|
|
355
|
+
.map(property => '`' + property.column + '`' + (property.reverse ? ' DESC' : ''))
|
|
343
356
|
.join(', ')
|
|
344
357
|
: '';
|
|
345
358
|
const rows = await connection.query('SELECT ' + propertiesSql + ' FROM `' + depends.storeOf(type) + '`' + sql + sort + limit + offset, Object.values(values));
|
package/package.json
CHANGED
package/src/mysql.d.ts
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { AnyObject } from '@itrocks/class-type';
|
|
2
|
+
import { KeyOf } from '@itrocks/class-type';
|
|
3
|
+
import { ObjectOrType } from '@itrocks/class-type';
|
|
4
|
+
import { Type } from '@itrocks/class-type';
|
|
5
|
+
import { DataSource } from '@itrocks/storage';
|
|
6
|
+
import { Entity } from '@itrocks/storage';
|
|
7
|
+
import { MayEntity } from '@itrocks/storage';
|
|
8
|
+
import { Identifier } from '@itrocks/storage';
|
|
9
|
+
import { Options } from '@itrocks/storage';
|
|
10
|
+
import { SearchType } from '@itrocks/storage';
|
|
11
|
+
import { Connection } from 'mariadb';
|
|
12
|
+
export declare const DEBUG = false;
|
|
13
|
+
interface Dependencies<QF extends object = object> {
|
|
14
|
+
applyReadTransformer: <T extends object>(record: AnyObject, property: KeyOf<T>, object: T) => any;
|
|
15
|
+
applySaveTransformer: <T extends object>(object: T, property: KeyOf<T>, record: AnyObject) => any;
|
|
16
|
+
columnOf: (property: string) => string;
|
|
17
|
+
componentOf: <T extends object>(target: T, property: KeyOf<T>) => boolean;
|
|
18
|
+
ignoreTransformedValue: any;
|
|
19
|
+
QueryFunction: Type<QF>;
|
|
20
|
+
queryFunctionCall: (value: QF) => [any, string];
|
|
21
|
+
storeOf: <T extends object>(target: ObjectOrType<T>) => string | false;
|
|
22
|
+
}
|
|
23
|
+
export declare function joinTableName(object1: string | ObjectOrType, object2: string | ObjectOrType): string;
|
|
24
|
+
export declare function mysqlDependsOn<QF extends object = object>(dependencies: Partial<Dependencies<QF>>): void;
|
|
25
|
+
export declare class Mysql extends DataSource {
|
|
26
|
+
config: {
|
|
27
|
+
host: string;
|
|
28
|
+
user: string;
|
|
29
|
+
password: string;
|
|
30
|
+
database: string;
|
|
31
|
+
};
|
|
32
|
+
connection?: Connection;
|
|
33
|
+
saveQueue: WeakMap<object, Promise<void | Entity<object>>>;
|
|
34
|
+
constructor(config: {
|
|
35
|
+
host: string;
|
|
36
|
+
user: string;
|
|
37
|
+
password: string;
|
|
38
|
+
database: string;
|
|
39
|
+
});
|
|
40
|
+
connect(): Promise<Connection>;
|
|
41
|
+
count<T extends object>(type: Type<T>, search?: SearchType<T>): Promise<number>;
|
|
42
|
+
delete<T extends object>(object: Entity<T>, property?: KeyOf<Entity<T>>): Promise<T>;
|
|
43
|
+
deleteId<T extends object>(type: ObjectOrType<T>, id: any, property?: KeyOf<Entity<T>>): Promise<void>;
|
|
44
|
+
deleteRelatedId<T extends Entity>(object: T, property: KeyOf<T>, id: Identifier): Promise<void>;
|
|
45
|
+
insert<T extends object>(object: T): Promise<Entity<T>>;
|
|
46
|
+
insertRelatedId<T extends Entity>(object: T, property: KeyOf<T>, id: Identifier): Promise<void>;
|
|
47
|
+
propertiesToSearchSql(search: AnyObject): string;
|
|
48
|
+
propertiesToSql(object: object): string;
|
|
49
|
+
propertiesToSqlSelect<T extends object>(type: Type<T>): string;
|
|
50
|
+
query<T extends object>(type: Type<T>, query: string, values?: any): Promise<Awaited<T & {
|
|
51
|
+
id: Identifier;
|
|
52
|
+
}>[]>;
|
|
53
|
+
read<T extends object>(type: Type<T>, id: Identifier): Promise<Entity<T>>;
|
|
54
|
+
readCollection<T extends object, PT extends object>(object: Entity<T>, property: KeyOf<T>, type?: Type<PT>): Promise<Awaited<PT & {
|
|
55
|
+
id: Identifier;
|
|
56
|
+
}>[]>;
|
|
57
|
+
readCollectionIds<T extends object, PT extends object>(object: Entity<T>, property: KeyOf<T>, type?: Type<PT>): Promise<Identifier[]>;
|
|
58
|
+
readMultiple<T extends object>(type: Type<T>, ids: Identifier[]): Promise<Entity<T>[]>;
|
|
59
|
+
runSerialized<T extends object>(object: MayEntity<T>, task: () => Promise<Entity<T>>): Promise<Entity<T>>;
|
|
60
|
+
save<T extends object>(object: MayEntity<T>): Promise<Entity<T>>;
|
|
61
|
+
saveCollection<T extends object>(object: Entity<T>, property: KeyOf<T>, value: (Identifier | MayEntity)[]): Promise<void>;
|
|
62
|
+
saveComponents<T extends object>(object: Entity<T>, property: KeyOf<T>, components: (Identifier | MayEntity)[]): Promise<void>;
|
|
63
|
+
saveLinks<T extends object>(object: Entity<T>, property: KeyOf<T>, links: (Identifier | MayEntity)[]): Promise<void>;
|
|
64
|
+
search<T extends object>(type: Type<T>, search?: SearchType<T>, options?: Options): Promise<Entity<T>[]>;
|
|
65
|
+
update<T extends object>(object: Entity<T>): Promise<Entity<T>>;
|
|
66
|
+
valuesFromDb<T extends object>(record: Entity<T>, type: Type<T>): Promise<Entity<T>>;
|
|
67
|
+
valuesToDb<T extends object>(object: T): Promise<[AnyObject, Function[]]>;
|
|
68
|
+
}
|
|
69
|
+
export {};
|
package/src/mysql.js
ADDED
|
@@ -0,0 +1,404 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Mysql = exports.DEBUG = void 0;
|
|
4
|
+
exports.joinTableName = joinTableName;
|
|
5
|
+
exports.mysqlDependsOn = mysqlDependsOn;
|
|
6
|
+
const class_type_1 = require("@itrocks/class-type");
|
|
7
|
+
const class_type_2 = require("@itrocks/class-type");
|
|
8
|
+
const class_type_3 = require("@itrocks/class-type");
|
|
9
|
+
const class_type_4 = require("@itrocks/class-type");
|
|
10
|
+
const composition_1 = require("@itrocks/composition");
|
|
11
|
+
const property_type_1 = require("@itrocks/property-type");
|
|
12
|
+
const reflect_1 = require("@itrocks/reflect");
|
|
13
|
+
const reflect_2 = require("@itrocks/reflect");
|
|
14
|
+
const sort_1 = require("@itrocks/sort");
|
|
15
|
+
const sort_2 = require("@itrocks/sort");
|
|
16
|
+
const storage_1 = require("@itrocks/storage");
|
|
17
|
+
const storage_2 = require("@itrocks/storage");
|
|
18
|
+
const storage_3 = require("@itrocks/storage");
|
|
19
|
+
const mariadb_1 = require("mariadb");
|
|
20
|
+
exports.DEBUG = false;
|
|
21
|
+
const depends = {
|
|
22
|
+
applyReadTransformer: (record, property) => record[property],
|
|
23
|
+
applySaveTransformer: (object, property) => object[property],
|
|
24
|
+
columnOf: name => name.toLowerCase(),
|
|
25
|
+
componentOf: () => false,
|
|
26
|
+
ignoreTransformedValue: Symbol('ignoreTransformedValue'),
|
|
27
|
+
QueryFunction: class {
|
|
28
|
+
},
|
|
29
|
+
queryFunctionCall: () => [undefined, ' = ?'],
|
|
30
|
+
storeOf: target => (0, class_type_4.typeOf)(target).name.toLowerCase()
|
|
31
|
+
};
|
|
32
|
+
function joinTableName(object1, object2) {
|
|
33
|
+
if (typeof object1 !== 'string')
|
|
34
|
+
object1 = depends.storeOf(object1);
|
|
35
|
+
if (typeof object2 !== 'string')
|
|
36
|
+
object2 = depends.storeOf(object2);
|
|
37
|
+
return [object1, object2].sort().join('_');
|
|
38
|
+
}
|
|
39
|
+
function mysqlDependsOn(dependencies) {
|
|
40
|
+
Object.assign(depends, dependencies);
|
|
41
|
+
}
|
|
42
|
+
class Mysql extends storage_1.DataSource {
|
|
43
|
+
config;
|
|
44
|
+
connection;
|
|
45
|
+
saveQueue = new WeakMap();
|
|
46
|
+
constructor(config) {
|
|
47
|
+
super();
|
|
48
|
+
this.config = config;
|
|
49
|
+
}
|
|
50
|
+
async connect() {
|
|
51
|
+
const mariaDbConfig = Object.assign(this.config, {
|
|
52
|
+
allowPublicKeyRetrieval: true,
|
|
53
|
+
dateStrings: false
|
|
54
|
+
});
|
|
55
|
+
return this.connection = await (0, mariadb_1.createConnection)(mariaDbConfig);
|
|
56
|
+
}
|
|
57
|
+
async count(type, search = {}) {
|
|
58
|
+
const connection = this.connection ?? await this.connect();
|
|
59
|
+
const localSearch = { ...search };
|
|
60
|
+
Object.setPrototypeOf(localSearch, type.prototype);
|
|
61
|
+
const sql = this.propertiesToSearchSql(localSearch);
|
|
62
|
+
const [values] = await this.valuesToDb(localSearch);
|
|
63
|
+
if (exports.DEBUG)
|
|
64
|
+
console.log('SELECT COUNT(*) FROM `' + depends.storeOf(type) + '`' + sql, JSON.stringify(values));
|
|
65
|
+
const row = (await connection.query('SELECT COUNT(*) `count` FROM `' + depends.storeOf(type) + '`' + sql, Object.values(values)))[0];
|
|
66
|
+
return row?.count;
|
|
67
|
+
}
|
|
68
|
+
async delete(object, property = 'id') {
|
|
69
|
+
await this.deleteId(object, object[property], property);
|
|
70
|
+
return this.disconnectObject(object);
|
|
71
|
+
}
|
|
72
|
+
async deleteId(type, id, property = 'id') {
|
|
73
|
+
const connection = this.connection ?? await this.connect();
|
|
74
|
+
if (exports.DEBUG)
|
|
75
|
+
console.log('DELETE FROM `' + depends.storeOf(type) + '` WHERE `' + depends.columnOf(property) + '` = ?', [id]);
|
|
76
|
+
await connection.query('DELETE FROM `' + depends.storeOf(type) + '` WHERE `' + depends.columnOf(property) + '` = ?', [id]);
|
|
77
|
+
}
|
|
78
|
+
async deleteRelatedId(object, property, id) {
|
|
79
|
+
const connection = this.connection ?? await this.connect();
|
|
80
|
+
const objectTable = depends.storeOf(object);
|
|
81
|
+
const propertyTable = depends.storeOf(new reflect_2.ReflectProperty(object, property).collectionType.elementType.type);
|
|
82
|
+
if (!objectTable || !propertyTable) {
|
|
83
|
+
throw 'Collection objects are not stored';
|
|
84
|
+
}
|
|
85
|
+
const joinTable = joinTableName(objectTable, propertyTable);
|
|
86
|
+
const query = 'DELETE FROM `' + joinTable + '` WHERE ' + objectTable + '_id = ? AND ' + propertyTable + '_id = ?';
|
|
87
|
+
const values = [object.id, id];
|
|
88
|
+
if (exports.DEBUG)
|
|
89
|
+
console.log(query, JSON.stringify(values));
|
|
90
|
+
connection.query(query, values);
|
|
91
|
+
}
|
|
92
|
+
async insert(object) {
|
|
93
|
+
const connection = this.connection ?? await this.connect();
|
|
94
|
+
const [values, deferred] = await this.valuesToDb(object);
|
|
95
|
+
const sql = this.propertiesToSql(values);
|
|
96
|
+
const query = 'INSERT INTO `' + depends.storeOf(object) + '` SET ' + sql;
|
|
97
|
+
if (exports.DEBUG)
|
|
98
|
+
console.log(query, JSON.stringify(Object.values(values)));
|
|
99
|
+
const result = await connection.query(query, Object.values(values));
|
|
100
|
+
const id = result.insertId;
|
|
101
|
+
const entity = this.connectObject(object, ((id >= Number.MIN_SAFE_INTEGER) && (id <= Number.MAX_SAFE_INTEGER)) ? Number(id) : id);
|
|
102
|
+
for (const callback of deferred) {
|
|
103
|
+
await callback(object);
|
|
104
|
+
}
|
|
105
|
+
return entity;
|
|
106
|
+
}
|
|
107
|
+
async insertRelatedId(object, property, id) {
|
|
108
|
+
const connection = this.connection ?? await this.connect();
|
|
109
|
+
const objectTable = depends.storeOf(object);
|
|
110
|
+
const propertyTable = depends.storeOf(new reflect_2.ReflectProperty(object, property).collectionType.elementType.type);
|
|
111
|
+
if (!objectTable || !propertyTable) {
|
|
112
|
+
throw 'Collection objects are not stored';
|
|
113
|
+
}
|
|
114
|
+
const joinTable = joinTableName(objectTable, propertyTable);
|
|
115
|
+
const query = 'INSERT INTO `' + joinTable + '` SET ' + objectTable + '_id = ?, ' + propertyTable + '_id = ?';
|
|
116
|
+
const values = [object.id, id];
|
|
117
|
+
if (exports.DEBUG)
|
|
118
|
+
console.log(query, JSON.stringify(values));
|
|
119
|
+
connection.query(query, values);
|
|
120
|
+
}
|
|
121
|
+
propertiesToSearchSql(search) {
|
|
122
|
+
const sql = Object.entries(search)
|
|
123
|
+
.map(([name, value]) => {
|
|
124
|
+
let sql;
|
|
125
|
+
if (value instanceof depends.QueryFunction) {
|
|
126
|
+
[value, sql] = depends.queryFunctionCall(value);
|
|
127
|
+
search[name] = value;
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
sql = ' = ?';
|
|
131
|
+
}
|
|
132
|
+
if ((typeof value)[0] === 'o') {
|
|
133
|
+
name = Array.isArray(value) ? 'id' : (name + '_id');
|
|
134
|
+
}
|
|
135
|
+
return '`' + depends.columnOf(name) + '`' + sql;
|
|
136
|
+
})
|
|
137
|
+
.join(' AND ');
|
|
138
|
+
return sql.length
|
|
139
|
+
? ' WHERE ' + sql
|
|
140
|
+
: '';
|
|
141
|
+
}
|
|
142
|
+
propertiesToSql(object) {
|
|
143
|
+
return Object.keys(object).map(name => '`' + depends.columnOf(name) + '` = ?').join(', ');
|
|
144
|
+
}
|
|
145
|
+
propertiesToSqlSelect(type) {
|
|
146
|
+
const sql = ['id'];
|
|
147
|
+
for (const property of new reflect_1.ReflectClass(type).properties) {
|
|
148
|
+
const propertyType = property.type;
|
|
149
|
+
if (propertyType instanceof property_type_1.CollectionType)
|
|
150
|
+
continue;
|
|
151
|
+
const propertyName = ((0, class_type_3.isAnyType)(propertyType.type) && depends.storeOf(propertyType.type))
|
|
152
|
+
? property.name + 'Id'
|
|
153
|
+
: property.name;
|
|
154
|
+
const columnName = depends.columnOf(propertyName);
|
|
155
|
+
sql.push((columnName.length !== propertyName.length)
|
|
156
|
+
? ('`' + columnName + '` `' + propertyName + '`')
|
|
157
|
+
: ('`' + propertyName + '`'));
|
|
158
|
+
}
|
|
159
|
+
return sql.join(', ');
|
|
160
|
+
}
|
|
161
|
+
async query(type, query, values) {
|
|
162
|
+
const connection = this.connection ?? await this.connect();
|
|
163
|
+
if (exports.DEBUG)
|
|
164
|
+
console.log(query, values);
|
|
165
|
+
const rows = await connection.query(query, values);
|
|
166
|
+
return Promise.all(rows.map(row => this.valuesFromDb(row, type)));
|
|
167
|
+
}
|
|
168
|
+
async read(type, id) {
|
|
169
|
+
const connection = this.connection ?? await this.connect();
|
|
170
|
+
const propertiesSql = this.propertiesToSqlSelect(type);
|
|
171
|
+
if (exports.DEBUG)
|
|
172
|
+
console.log('SELECT ' + propertiesSql + ' FROM `' + depends.storeOf(type) + '` WHERE id = ?', [id]);
|
|
173
|
+
const rows = await connection.query('SELECT ' + propertiesSql + ' FROM `' + depends.storeOf(type) + '` WHERE id = ?', [id]);
|
|
174
|
+
return this.valuesFromDb(rows[0], type);
|
|
175
|
+
}
|
|
176
|
+
async readCollection(object, property, type) {
|
|
177
|
+
type ??= new reflect_2.ReflectProperty(object, property).collectionType.elementType.type;
|
|
178
|
+
const connection = this.connection ?? await this.connect();
|
|
179
|
+
const propertiesSql = this.propertiesToSqlSelect(type);
|
|
180
|
+
const objectTable = depends.storeOf(object);
|
|
181
|
+
const propertyTable = depends.storeOf(type);
|
|
182
|
+
if (!objectTable || !propertyTable) {
|
|
183
|
+
throw 'Collection objects are not stored';
|
|
184
|
+
}
|
|
185
|
+
let query;
|
|
186
|
+
if (depends.componentOf(object, property)) {
|
|
187
|
+
query = 'SELECT ' + propertiesSql + ' FROM `' + propertyTable + '` WHERE ' + objectTable + '_id = ?';
|
|
188
|
+
}
|
|
189
|
+
else {
|
|
190
|
+
const joinTable = joinTableName(objectTable, propertyTable);
|
|
191
|
+
query = 'SELECT `' + propertyTable + '`.' + propertiesSql + ' FROM `' + propertyTable + '`'
|
|
192
|
+
+ ' INNER JOIN `' + joinTable + '` ON `' + joinTable + '`.' + propertyTable + '_id = `' + propertyTable + '`.id'
|
|
193
|
+
+ ' WHERE `' + joinTable + '`.' + objectTable + '_id = ?';
|
|
194
|
+
}
|
|
195
|
+
const rows = await connection.query(query, [object.id]);
|
|
196
|
+
return Promise.all(rows.map(row => this.valuesFromDb(row, type)));
|
|
197
|
+
}
|
|
198
|
+
async readCollectionIds(object, property, type) {
|
|
199
|
+
type ??= new reflect_2.ReflectProperty(object, property).collectionType.elementType.type;
|
|
200
|
+
const connection = this.connection ?? await this.connect();
|
|
201
|
+
const objectTable = depends.storeOf(object);
|
|
202
|
+
const propertyTable = depends.storeOf(type);
|
|
203
|
+
if (!objectTable || !propertyTable) {
|
|
204
|
+
throw 'Collection objects are not stored';
|
|
205
|
+
}
|
|
206
|
+
let query;
|
|
207
|
+
if (depends.componentOf(object, property)) {
|
|
208
|
+
query = 'SELECT id FROM `' + propertyTable + '` WHERE ' + objectTable + '_id = ?';
|
|
209
|
+
}
|
|
210
|
+
else {
|
|
211
|
+
const joinTable = joinTableName(objectTable, propertyTable);
|
|
212
|
+
query = 'SELECT ' + propertyTable + '_id id FROM `' + joinTable + '`'
|
|
213
|
+
+ ' WHERE `' + joinTable + '`.' + objectTable + '_id = ?';
|
|
214
|
+
}
|
|
215
|
+
const rows = await connection.query(query, [object.id]);
|
|
216
|
+
return Promise.all(rows.map(row => row.id));
|
|
217
|
+
}
|
|
218
|
+
async readMultiple(type, ids) {
|
|
219
|
+
if (!ids.length)
|
|
220
|
+
return [];
|
|
221
|
+
const connection = this.connection ?? await this.connect();
|
|
222
|
+
const propertiesSql = this.propertiesToSqlSelect(type);
|
|
223
|
+
const questionMarks = Array(ids.length).fill('?').join(', ');
|
|
224
|
+
if (exports.DEBUG)
|
|
225
|
+
console.log('SELECT ' + propertiesSql + ' FROM `' + depends.storeOf(type) + '` WHERE id IN (' + questionMarks + ')', ids);
|
|
226
|
+
const rows = await connection.query('SELECT ' + propertiesSql + ' FROM `' + depends.storeOf(type) + '` WHERE id IN (' + questionMarks + ')', ids);
|
|
227
|
+
return Promise.all(rows.map(row => this.valuesFromDb(row, type)));
|
|
228
|
+
}
|
|
229
|
+
async runSerialized(object, task) {
|
|
230
|
+
const prev = this.saveQueue.get(object) || Promise.resolve();
|
|
231
|
+
const next = prev.then(task, task);
|
|
232
|
+
this.saveQueue.set(object, next.then(() => { this.saveQueue.delete(object); }, () => { this.saveQueue.delete(object); }));
|
|
233
|
+
return next;
|
|
234
|
+
}
|
|
235
|
+
async save(object) {
|
|
236
|
+
return this.runSerialized(object, async () => {
|
|
237
|
+
return this.isObjectConnected(object)
|
|
238
|
+
? this.update(object)
|
|
239
|
+
: this.insert(object);
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
async saveCollection(object, property, value) {
|
|
243
|
+
if (property.endsWith('Ids')) {
|
|
244
|
+
property = property.slice(0, -3);
|
|
245
|
+
}
|
|
246
|
+
return depends.componentOf(object, property)
|
|
247
|
+
? this.saveComponents(object, property, value)
|
|
248
|
+
: this.saveLinks(object, property, value);
|
|
249
|
+
}
|
|
250
|
+
async saveComponents(object, property, components) {
|
|
251
|
+
const connection = this.connection ?? await this.connect();
|
|
252
|
+
const propertyType = new reflect_2.ReflectProperty(object, property).collectionType.elementType.type;
|
|
253
|
+
const stored = await this.readCollectionIds(object, property, propertyType);
|
|
254
|
+
const saved = new Array;
|
|
255
|
+
let compositeProperty;
|
|
256
|
+
for (const component of components) {
|
|
257
|
+
if (typeof component !== 'object') {
|
|
258
|
+
saved.push(component);
|
|
259
|
+
continue;
|
|
260
|
+
}
|
|
261
|
+
if (compositeProperty === undefined) {
|
|
262
|
+
const objectType = (0, class_type_4.typeOf)(object);
|
|
263
|
+
for (const candidate of new reflect_1.ReflectClass(component).properties) {
|
|
264
|
+
if (!(0, composition_1.compositeOf)(component, candidate.name))
|
|
265
|
+
continue;
|
|
266
|
+
const candidateType = candidate.type.type;
|
|
267
|
+
if (!(0, class_type_3.isAnyType)(candidateType))
|
|
268
|
+
continue;
|
|
269
|
+
if (!(0, class_type_1.inherits)(objectType, candidateType))
|
|
270
|
+
continue;
|
|
271
|
+
compositeProperty = candidate;
|
|
272
|
+
break;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
if (compositeProperty) {
|
|
276
|
+
// @ts-ignore TS2322 Don't understand this error
|
|
277
|
+
component[compositeProperty.name] = object;
|
|
278
|
+
}
|
|
279
|
+
saved.push((await this.save(component)).id);
|
|
280
|
+
}
|
|
281
|
+
let componentTable;
|
|
282
|
+
for (const storedId of stored) {
|
|
283
|
+
if (saved.includes(storedId))
|
|
284
|
+
continue;
|
|
285
|
+
if (!componentTable) {
|
|
286
|
+
componentTable = depends.storeOf(propertyType);
|
|
287
|
+
if (!componentTable) {
|
|
288
|
+
throw 'Missing @Store on type ' + propertyType.name
|
|
289
|
+
+ ' used by @Component ' + new reflect_1.ReflectClass(object).name + '.' + property;
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
await connection.query('DELETE FROM `' + componentTable + '` WHERE id = ?', [storedId]);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
async saveLinks(object, property, links) {
|
|
296
|
+
const connection = this.connection ?? await this.connect();
|
|
297
|
+
const objectTable = depends.storeOf(object);
|
|
298
|
+
const propertyType = new reflect_2.ReflectProperty(object, property).collectionType.elementType.type;
|
|
299
|
+
const propertyTable = depends.storeOf(propertyType);
|
|
300
|
+
const linkColumn = depends.columnOf(propertyTable) + '_id';
|
|
301
|
+
const linkTable = joinTableName(objectTable, propertyTable);
|
|
302
|
+
const objectColumn = depends.columnOf(objectTable) + '_id';
|
|
303
|
+
const objectId = object.id;
|
|
304
|
+
const stored = await this.readCollectionIds(object, property, propertyType);
|
|
305
|
+
const saved = new Array;
|
|
306
|
+
for (const link of links) {
|
|
307
|
+
const linkId = (typeof link === 'object')
|
|
308
|
+
? (this.isObjectConnected(link) ? link.id : (await this.save(link)).id)
|
|
309
|
+
: link;
|
|
310
|
+
saved.push(linkId);
|
|
311
|
+
if (stored.includes(linkId))
|
|
312
|
+
continue;
|
|
313
|
+
await connection.query('INSERT INTO `' + linkTable + '` SET ' + objectColumn + ' = ?, ' + linkColumn + ' = ?', [objectId, linkId]);
|
|
314
|
+
stored.push(linkId);
|
|
315
|
+
}
|
|
316
|
+
for (const storedId of stored) {
|
|
317
|
+
if (saved.includes(storedId))
|
|
318
|
+
continue;
|
|
319
|
+
await connection.query('DELETE FROM `' + linkTable + '` WHERE ' + objectColumn + ' = ? AND ' + linkColumn + ' = ?', [objectId, storedId]);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
async search(type, search = {}, options) {
|
|
323
|
+
const connection = this.connection ?? await this.connect();
|
|
324
|
+
const propertiesSql = this.propertiesToSqlSelect(type);
|
|
325
|
+
let limitOption = undefined;
|
|
326
|
+
let sortOption = undefined;
|
|
327
|
+
for (const option of this.options(options)) {
|
|
328
|
+
if (option === storage_3.Sort) {
|
|
329
|
+
sortOption = new storage_3.Sort((0, sort_2.sortOf)(type));
|
|
330
|
+
}
|
|
331
|
+
if (option instanceof storage_2.Limit) {
|
|
332
|
+
limitOption = option;
|
|
333
|
+
}
|
|
334
|
+
if (option instanceof storage_3.Sort) {
|
|
335
|
+
sortOption = option.properties.length ? option : new storage_3.Sort((0, sort_2.sortOf)(type));
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
const localSearch = { ...search };
|
|
339
|
+
Object.setPrototypeOf(localSearch, type.prototype);
|
|
340
|
+
const sql = this.propertiesToSearchSql(localSearch);
|
|
341
|
+
const [values] = await this.valuesToDb(localSearch);
|
|
342
|
+
if (exports.DEBUG)
|
|
343
|
+
console.log('SELECT ' + propertiesSql + ' FROM `' + depends.storeOf(type) + '`' + sql, JSON.stringify(values));
|
|
344
|
+
const limit = limitOption?.limit ? ' LIMIT ' + limitOption.limit : '';
|
|
345
|
+
const offset = limitOption?.offset ? ' OFFSET ' + limitOption.offset : '';
|
|
346
|
+
const sort = sortOption?.properties.length
|
|
347
|
+
? ' ORDER BY '
|
|
348
|
+
+ sortOption.properties
|
|
349
|
+
.map(property => '`' + property + '`' + (property instanceof sort_1.Reverse ? ' DESC' : ''))
|
|
350
|
+
.join(', ')
|
|
351
|
+
: '';
|
|
352
|
+
const rows = await connection.query('SELECT ' + propertiesSql + ' FROM `' + depends.storeOf(type) + '`' + sql + sort + limit + offset, Object.values(values));
|
|
353
|
+
return Promise.all(rows.map(row => this.valuesFromDb(row, type)));
|
|
354
|
+
}
|
|
355
|
+
async update(object) {
|
|
356
|
+
const connection = this.connection ?? await this.connect();
|
|
357
|
+
const [values, deferred] = await this.valuesToDb(object);
|
|
358
|
+
const sql = this.propertiesToSql(values);
|
|
359
|
+
const query = 'UPDATE `' + depends.storeOf(object) + '` SET ' + sql + ' WHERE id = ?';
|
|
360
|
+
if (exports.DEBUG)
|
|
361
|
+
console.log(query, JSON.stringify(Object.values(values).concat([object.id])));
|
|
362
|
+
await connection.query(query, Object.values(values).concat([object.id]));
|
|
363
|
+
for (const callback of deferred) {
|
|
364
|
+
await callback(object);
|
|
365
|
+
}
|
|
366
|
+
return object;
|
|
367
|
+
}
|
|
368
|
+
async valuesFromDb(record, type) {
|
|
369
|
+
const object = (new type);
|
|
370
|
+
let property;
|
|
371
|
+
for (property in record) {
|
|
372
|
+
const value = await depends.applyReadTransformer(record, property, object);
|
|
373
|
+
if (value === depends.ignoreTransformedValue)
|
|
374
|
+
continue;
|
|
375
|
+
object[property] = value;
|
|
376
|
+
if (property.endsWith('Id')) {
|
|
377
|
+
delete object[property.slice(0, -2)];
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
return object;
|
|
381
|
+
}
|
|
382
|
+
async valuesToDb(object) {
|
|
383
|
+
const deferred = [];
|
|
384
|
+
const record = {};
|
|
385
|
+
for (const property of Object.keys(object)) {
|
|
386
|
+
const value = await depends.applySaveTransformer(object, property, record);
|
|
387
|
+
if (value === depends.ignoreTransformedValue) {
|
|
388
|
+
continue;
|
|
389
|
+
}
|
|
390
|
+
if (Array.isArray(value)) {
|
|
391
|
+
deferred.push((object) => this.saveCollection(object, property, value));
|
|
392
|
+
continue;
|
|
393
|
+
}
|
|
394
|
+
if ((0, class_type_2.isAnyFunction)(value)) {
|
|
395
|
+
deferred.push(value);
|
|
396
|
+
continue;
|
|
397
|
+
}
|
|
398
|
+
record[depends.columnOf(property)] = value;
|
|
399
|
+
}
|
|
400
|
+
return [record, deferred];
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
exports.Mysql = Mysql;
|
|
404
|
+
//# sourceMappingURL=mysql.js.map
|