sonamu 0.2.48 → 0.2.49
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/bin/cli-wrapper.js +12 -2
- package/dist/bin/cli-wrapper.js.map +1 -1
- package/dist/bin/cli.js +54 -62
- package/dist/bin/cli.js.map +1 -1
- package/dist/{chunk-76VBQWGE.js → chunk-HATLA54Z.js} +167 -97
- package/dist/chunk-HATLA54Z.js.map +1 -0
- package/dist/index.d.ts +15 -7
- package/dist/index.js +3 -3
- package/package.json +1 -1
- package/src/api/sonamu.ts +3 -2
- package/src/bin/cli-wrapper.ts +20 -1
- package/src/bin/cli.ts +5 -15
- package/src/database/_batch_update.ts +29 -14
- package/src/database/upsert-builder.ts +56 -42
- package/src/testing/fixture-manager.ts +122 -36
- package/src/types/types.ts +3 -2
- package/src/utils/utils.ts +15 -13
- package/dist/chunk-76VBQWGE.js.map +0 -1
package/dist/index.d.ts
CHANGED
|
@@ -761,6 +761,7 @@ type FixtureRecord = {
|
|
|
761
761
|
fetchedRecords: string[];
|
|
762
762
|
belongsRecords: string[];
|
|
763
763
|
target?: FixtureRecord;
|
|
764
|
+
unique?: FixtureRecord;
|
|
764
765
|
override?: boolean;
|
|
765
766
|
};
|
|
766
767
|
type FixtureImportResult = {
|
|
@@ -1100,7 +1101,10 @@ declare class SonamuClass {
|
|
|
1100
1101
|
set secrets(secrets: SonamuSecrets);
|
|
1101
1102
|
get secrets(): SonamuSecrets | null;
|
|
1102
1103
|
init(doSilent?: boolean, enableSync?: boolean, apiRootPath?: string): Promise<void>;
|
|
1103
|
-
withFastify(server: FastifyInstance<Server, IncomingMessage, ServerResponse>, config: SonamuFastifyConfig
|
|
1104
|
+
withFastify(server: FastifyInstance<Server, IncomingMessage, ServerResponse>, config: SonamuFastifyConfig, options?: {
|
|
1105
|
+
enableSync?: boolean;
|
|
1106
|
+
doSilent?: boolean;
|
|
1107
|
+
}): Promise<void>;
|
|
1104
1108
|
destroy(): Promise<void>;
|
|
1105
1109
|
}
|
|
1106
1110
|
declare const Sonamu: SonamuClass;
|
|
@@ -1145,12 +1149,12 @@ declare class UpsertBuilder {
|
|
|
1145
1149
|
register<T extends string>(tableName: string, row: {
|
|
1146
1150
|
[key in T]?: UBRef | string | number | boolean | bigint | null | object | unknown;
|
|
1147
1151
|
}): UBRef;
|
|
1148
|
-
upsert(wdb: Knex, tableName: string): Promise<number[]>;
|
|
1149
|
-
insertOnly(wdb: Knex, tableName: string): Promise<number[]>;
|
|
1150
|
-
upsertOrInsert(wdb: Knex, tableName: string, mode: "upsert" | "insert"): Promise<number[]>;
|
|
1152
|
+
upsert(wdb: Knex, tableName: string, chunkSize?: number): Promise<number[]>;
|
|
1153
|
+
insertOnly(wdb: Knex, tableName: string, chunkSize?: number): Promise<number[]>;
|
|
1154
|
+
upsertOrInsert(wdb: Knex, tableName: string, mode: "upsert" | "insert", chunkSize?: number): Promise<number[]>;
|
|
1151
1155
|
updateBatch(wdb: Knex, tableName: string, options?: {
|
|
1152
1156
|
chunkSize?: number;
|
|
1153
|
-
where?: string;
|
|
1157
|
+
where?: string | string[];
|
|
1154
1158
|
}): Promise<void>;
|
|
1155
1159
|
}
|
|
1156
1160
|
|
|
@@ -1452,14 +1456,18 @@ declare class FixtureManagerClass {
|
|
|
1452
1456
|
getImportQueries(entityId: string, field: string, id: number): Promise<string[]>;
|
|
1453
1457
|
destory(): Promise<void>;
|
|
1454
1458
|
getFixtures(sourceDBName: keyof SonamuDBConfig, targetDBName: keyof SonamuDBConfig, searchOptions: FixtureSearchOptions): Promise<FixtureRecord[]>;
|
|
1455
|
-
createFixtureRecord(entity: Entity, row: any,
|
|
1456
|
-
|
|
1459
|
+
createFixtureRecord(entity: Entity, row: any, options?: {
|
|
1460
|
+
singleRecord?: boolean;
|
|
1461
|
+
_db?: Knex;
|
|
1462
|
+
}, visitedEntities?: Set<string>): Promise<FixtureRecord[]>;
|
|
1463
|
+
insertFixtures(dbName: keyof SonamuDBConfig, _fixtures: FixtureRecord[]): Promise<FixtureImportResult[]>;
|
|
1457
1464
|
private getInsertionOrder;
|
|
1458
1465
|
private prepareInsertData;
|
|
1459
1466
|
private buildDependencyGraph;
|
|
1460
1467
|
private insertFixture;
|
|
1461
1468
|
private handleManyToManyRelations;
|
|
1462
1469
|
addFixtureLoader(code: string): Promise<void>;
|
|
1470
|
+
private checkUniqueViolation;
|
|
1463
1471
|
}
|
|
1464
1472
|
declare const FixtureManager: FixtureManagerClass;
|
|
1465
1473
|
|
package/dist/index.js
CHANGED
|
@@ -80,13 +80,13 @@
|
|
|
80
80
|
|
|
81
81
|
|
|
82
82
|
|
|
83
|
-
var
|
|
83
|
+
var _chunkHATLA54Zjs = require('./chunk-HATLA54Z.js');
|
|
84
84
|
|
|
85
85
|
// src/exceptions/error-handler.ts
|
|
86
86
|
function setupErrorHandler(server) {
|
|
87
87
|
server.setErrorHandler((error, request, reply) => {
|
|
88
88
|
_nullishCoalesce(error.statusCode, () => ( (error.statusCode = 400)));
|
|
89
|
-
if (
|
|
89
|
+
if (_chunkHATLA54Zjs.isSoException.call(void 0, error) && error.payload && Array.isArray(error.payload)) {
|
|
90
90
|
const issues = error.payload;
|
|
91
91
|
const [issue] = issues;
|
|
92
92
|
const message = `${issue.message} (${issue.path.join("/")})`;
|
|
@@ -423,5 +423,5 @@ function unique(columns) {
|
|
|
423
423
|
|
|
424
424
|
|
|
425
425
|
|
|
426
|
-
exports.AlreadyProcessedException =
|
|
426
|
+
exports.AlreadyProcessedException = _chunkHATLA54Zjs.AlreadyProcessedException; exports.ApiParamType = _chunkHATLA54Zjs.ApiParamType; exports.BadRequestException = _chunkHATLA54Zjs.BadRequestException; exports.BaseModel = _chunkHATLA54Zjs.BaseModel; exports.BaseModelClass = _chunkHATLA54Zjs.BaseModelClass; exports.DB = _chunkHATLA54Zjs.DB; exports.DuplicateRowException = _chunkHATLA54Zjs.DuplicateRowException; exports.Entity = _chunkHATLA54Zjs.Entity; exports.EntityManager = _chunkHATLA54Zjs.EntityManager; exports.FixtureManager = _chunkHATLA54Zjs.FixtureManager; exports.FixtureManagerClass = _chunkHATLA54Zjs.FixtureManagerClass; exports.GenerateOptions = _chunkHATLA54Zjs.GenerateOptions; exports.InternalServerErrorException = _chunkHATLA54Zjs.InternalServerErrorException; exports.Migrator = _chunkHATLA54Zjs.Migrator; exports.NotFoundException = _chunkHATLA54Zjs.NotFoundException; exports.PathAndCode = _chunkHATLA54Zjs.PathAndCode; exports.RenderingNode = _chunkHATLA54Zjs.RenderingNode; exports.SQLDateTimeString = _chunkHATLA54Zjs.SQLDateTimeString; exports.ServiceUnavailableException = _chunkHATLA54Zjs.ServiceUnavailableException; exports.SoException = _chunkHATLA54Zjs.SoException; exports.Sonamu = _chunkHATLA54Zjs.Sonamu; exports.SonamuQueryMode = _chunkHATLA54Zjs.SonamuQueryMode; exports.Syncer = _chunkHATLA54Zjs.Syncer; exports.TargetNotFoundException = _chunkHATLA54Zjs.TargetNotFoundException; exports.TemplateKey = _chunkHATLA54Zjs.TemplateKey; exports.TemplateOptions = _chunkHATLA54Zjs.TemplateOptions; exports.UnauthorizedException = _chunkHATLA54Zjs.UnauthorizedException; exports.UpsertBuilder = _chunkHATLA54Zjs.UpsertBuilder; exports.api = _chunkHATLA54Zjs.api; exports.apiParamToTsCode = _chunkHATLA54Zjs.apiParamToTsCode; exports.apiParamTypeToTsType = _chunkHATLA54Zjs.apiParamTypeToTsType; exports.asArray = asArray; exports.findApiRootPath = _chunkHATLA54Zjs.findApiRootPath; exports.findAppRootPath = _chunkHATLA54Zjs.findAppRootPath; exports.getTextTypeLength = _chunkHATLA54Zjs.getTextTypeLength; exports.getZodObjectFromApi = _chunkHATLA54Zjs.getZodObjectFromApi; exports.getZodObjectFromApiParams = _chunkHATLA54Zjs.getZodObjectFromApiParams; exports.getZodTypeFromApiParamType = _chunkHATLA54Zjs.getZodTypeFromApiParamType; exports.globAsync = _chunkHATLA54Zjs.globAsync; exports.i = i; exports.importMultiple = _chunkHATLA54Zjs.importMultiple; exports.isBelongsToOneRelationProp = _chunkHATLA54Zjs.isBelongsToOneRelationProp; exports.isBigIntegerProp = _chunkHATLA54Zjs.isBigIntegerProp; exports.isBooleanProp = _chunkHATLA54Zjs.isBooleanProp; exports.isCustomJoinClause = _chunkHATLA54Zjs.isCustomJoinClause; exports.isDaemonServer = _chunkHATLA54Zjs.isDaemonServer; exports.isDateProp = _chunkHATLA54Zjs.isDateProp; exports.isDateTimeProp = _chunkHATLA54Zjs.isDateTimeProp; exports.isDecimalProp = _chunkHATLA54Zjs.isDecimalProp; exports.isDevelopment = _chunkHATLA54Zjs.isDevelopment; exports.isDoubleProp = _chunkHATLA54Zjs.isDoubleProp; exports.isEnumProp = _chunkHATLA54Zjs.isEnumProp; exports.isFloatProp = _chunkHATLA54Zjs.isFloatProp; exports.isHasManyRelationProp = _chunkHATLA54Zjs.isHasManyRelationProp; exports.isInDocker = _chunkHATLA54Zjs.isInDocker; exports.isIntegerProp = _chunkHATLA54Zjs.isIntegerProp; exports.isJsonProp = _chunkHATLA54Zjs.isJsonProp; exports.isKnexError = _chunkHATLA54Zjs.isKnexError; exports.isLocal = _chunkHATLA54Zjs.isLocal; exports.isManyToManyRelationProp = _chunkHATLA54Zjs.isManyToManyRelationProp; exports.isOneToOneRelationProp = _chunkHATLA54Zjs.isOneToOneRelationProp; exports.isProduction = _chunkHATLA54Zjs.isProduction; exports.isRefField = _chunkHATLA54Zjs.isRefField; exports.isRelationProp = _chunkHATLA54Zjs.isRelationProp; exports.isRemote = _chunkHATLA54Zjs.isRemote; exports.isSoException = _chunkHATLA54Zjs.isSoException; exports.isStaging = _chunkHATLA54Zjs.isStaging; exports.isStringProp = _chunkHATLA54Zjs.isStringProp; exports.isTest = _chunkHATLA54Zjs.isTest; exports.isTextProp = _chunkHATLA54Zjs.isTextProp; exports.isTimeProp = _chunkHATLA54Zjs.isTimeProp; exports.isTimestampProp = _chunkHATLA54Zjs.isTimestampProp; exports.isUuidProp = _chunkHATLA54Zjs.isUuidProp; exports.isVirtualProp = _chunkHATLA54Zjs.isVirtualProp; exports.nonNullable = _chunkHATLA54Zjs.nonNullable; exports.objToMap = objToMap; exports.p = p; exports.propNodeToZodTypeDef = _chunkHATLA54Zjs.propNodeToZodTypeDef; exports.propToZodTypeDef = _chunkHATLA54Zjs.propToZodTypeDef; exports.registeredApis = _chunkHATLA54Zjs.registeredApis; exports.serializeZodType = _chunkHATLA54Zjs.serializeZodType; exports.setupErrorHandler = setupErrorHandler; exports.unwrapPromiseOnce = _chunkHATLA54Zjs.unwrapPromiseOnce; exports.zArrayable = _chunkHATLA54Zjs.zArrayable; exports.zodTypeToTsTypeDef = _chunkHATLA54Zjs.zodTypeToTsTypeDef; exports.zodTypeToZodCode = _chunkHATLA54Zjs.zodTypeToZodCode;
|
|
427
427
|
//# sourceMappingURL=index.js.map
|
package/package.json
CHANGED
package/src/api/sonamu.ts
CHANGED
|
@@ -185,10 +185,11 @@ class SonamuClass {
|
|
|
185
185
|
|
|
186
186
|
async withFastify(
|
|
187
187
|
server: FastifyInstance<Server, IncomingMessage, ServerResponse>,
|
|
188
|
-
config: SonamuFastifyConfig
|
|
188
|
+
config: SonamuFastifyConfig,
|
|
189
|
+
options?: { enableSync?: boolean; doSilent?: boolean }
|
|
189
190
|
) {
|
|
190
191
|
if (this.isInitialized === false) {
|
|
191
|
-
await this.init();
|
|
192
|
+
await this.init(options?.doSilent, options?.enableSync);
|
|
192
193
|
}
|
|
193
194
|
|
|
194
195
|
// 전체 라우팅 리스트
|
package/src/bin/cli-wrapper.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env ts-node
|
|
2
2
|
|
|
3
3
|
import { spawnSync } from "child_process";
|
|
4
|
-
import { resolve } from "path";
|
|
4
|
+
import { extname, resolve } from "path";
|
|
5
5
|
import { existsSync, readFileSync } from "fs";
|
|
6
6
|
|
|
7
7
|
const cjsPath = resolve(__dirname, "bin/cli.js");
|
|
@@ -9,10 +9,29 @@ const esmPath = resolve(__dirname, "bin/cli.mjs");
|
|
|
9
9
|
|
|
10
10
|
const isESM = () => {
|
|
11
11
|
const packageJsonPath = resolve(process.cwd(), "package.json");
|
|
12
|
+
const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
|
|
13
|
+
|
|
14
|
+
// package.json에 "type": "module" 설정 확인
|
|
15
|
+
if (packageJson.type === "module") {
|
|
16
|
+
return true;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// 환경 변수에서 ESM 여부 확인
|
|
20
|
+
if (process.env.USE_ESM === "true") {
|
|
21
|
+
return true;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// package.json에 "type": "module" 설정
|
|
12
25
|
if (existsSync(packageJsonPath)) {
|
|
13
26
|
const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
|
|
14
27
|
return packageJson.type === "module";
|
|
15
28
|
}
|
|
29
|
+
|
|
30
|
+
// main 필드가 .mjs로 끝나는지 확인
|
|
31
|
+
if (packageJson.main && extname(packageJson.main) === ".mjs") {
|
|
32
|
+
return true;
|
|
33
|
+
}
|
|
34
|
+
|
|
16
35
|
return false;
|
|
17
36
|
};
|
|
18
37
|
|
package/src/bin/cli.ts
CHANGED
|
@@ -166,9 +166,13 @@ async function fixture_init() {
|
|
|
166
166
|
console.log("DUMP...");
|
|
167
167
|
const dumpFilename = `/tmp/sonamu-fixture-init-${Date.now()}.sql`;
|
|
168
168
|
const srcConn = srcConfig.connection as Knex.ConnectionConfig;
|
|
169
|
+
const migrationsDump = `/tmp/sonamu-fixture-init-migrations-${Date.now()}.sql`;
|
|
169
170
|
execSync(
|
|
170
171
|
`mysqldump -h${srcConn.host} -u${srcConn.user} -p${srcConn.password} --single-transaction -d --no-create-db --triggers ${srcConn.database} > ${dumpFilename}`
|
|
171
172
|
);
|
|
173
|
+
execSync(
|
|
174
|
+
`mysqldump -h${srcConn.host} -u${srcConn.user} -p${srcConn.password} --single-transaction --no-create-db --triggers ${srcConn.database} knex_migrations knex_migrations_lock > ${migrationsDump}`
|
|
175
|
+
);
|
|
172
176
|
|
|
173
177
|
// 2. 대상DB 각각에 대하여 존재여부 확인 후 붓기
|
|
174
178
|
for await (const { label, config, toSkip } of targets) {
|
|
@@ -200,21 +204,7 @@ async function fixture_init() {
|
|
|
200
204
|
execSync(`${mysqlCmd} -e 'DROP DATABASE IF EXISTS \`${conn.database}\`'`);
|
|
201
205
|
execSync(`${mysqlCmd} -e 'CREATE DATABASE \`${conn.database}\`'`);
|
|
202
206
|
execSync(`${mysqlCmd} ${conn.database} < ${dumpFilename}`);
|
|
203
|
-
|
|
204
|
-
// 3. knex migration 정보 복사
|
|
205
|
-
await Promise.all(
|
|
206
|
-
["knex_migrations", "knex_migrations_lock"].map(async (tableName) => {
|
|
207
|
-
const [table] = await db.raw(
|
|
208
|
-
`SHOW TABLES FROM \`${srcConn.database}\` LIKE '${tableName}'`
|
|
209
|
-
);
|
|
210
|
-
if (table?.length) {
|
|
211
|
-
await db.raw(
|
|
212
|
-
`INSERT INTO \`${conn.database}\`.${tableName}
|
|
213
|
-
SELECT * FROM \`${srcConn.database}\`.${tableName}`
|
|
214
|
-
);
|
|
215
|
-
}
|
|
216
|
-
})
|
|
217
|
-
);
|
|
207
|
+
execSync(`${mysqlCmd} ${conn.database} < ${migrationsDump}`);
|
|
218
208
|
|
|
219
209
|
await db.destroy();
|
|
220
210
|
}
|
|
@@ -13,7 +13,7 @@ export type RowWithId<Id extends string> = {
|
|
|
13
13
|
* Batch update rows in a table. Technically its a patch since it only updates the specified columns. Any omitted columns will not be affected
|
|
14
14
|
* @param knex
|
|
15
15
|
* @param tableName
|
|
16
|
-
* @param
|
|
16
|
+
* @param ids
|
|
17
17
|
* @param rows
|
|
18
18
|
* @param chunkSize
|
|
19
19
|
* @param trx
|
|
@@ -21,7 +21,7 @@ export type RowWithId<Id extends string> = {
|
|
|
21
21
|
export async function batchUpdate<Id extends string>(
|
|
22
22
|
knex: Knex,
|
|
23
23
|
tableName: string,
|
|
24
|
-
|
|
24
|
+
ids: Id[],
|
|
25
25
|
rows: RowWithId<Id>[],
|
|
26
26
|
chunkSize = 50,
|
|
27
27
|
trx: Knex.Transaction | null = null
|
|
@@ -35,7 +35,7 @@ export async function batchUpdate<Id extends string>(
|
|
|
35
35
|
chunk: RowWithId<Id>[],
|
|
36
36
|
transaction: Knex.Transaction
|
|
37
37
|
) => {
|
|
38
|
-
const sql = generateBatchUpdateSQL(knex, tableName, chunk,
|
|
38
|
+
const sql = generateBatchUpdateSQL(knex, tableName, chunk, ids);
|
|
39
39
|
return knex.raw(sql).transacting(transaction);
|
|
40
40
|
};
|
|
41
41
|
|
|
@@ -73,20 +73,30 @@ function generateBatchUpdateSQL<Id extends string>(
|
|
|
73
73
|
knex: Knex,
|
|
74
74
|
tableName: string,
|
|
75
75
|
data: Record<string, any>[],
|
|
76
|
-
|
|
76
|
+
identifiers: Id[]
|
|
77
77
|
) {
|
|
78
78
|
const keySet = generateKeySetFromData(data);
|
|
79
79
|
const bindings = [];
|
|
80
80
|
|
|
81
|
+
const invalidIdentifiers = identifiers.filter((id) => !keySet.has(id));
|
|
82
|
+
if (invalidIdentifiers.length > 0) {
|
|
83
|
+
throw new Error(
|
|
84
|
+
`Invalid identifiers: ${invalidIdentifiers.join(", ")}. Identifiers must exist in the data`
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
|
|
81
88
|
const cases = [];
|
|
82
89
|
for (const key of keySet) {
|
|
83
|
-
if (key
|
|
90
|
+
if (identifiers.includes(key as Id)) continue;
|
|
84
91
|
|
|
85
92
|
const rows = [];
|
|
86
93
|
for (const row of data) {
|
|
87
94
|
if (Object.hasOwnProperty.call(row, key)) {
|
|
88
|
-
|
|
89
|
-
|
|
95
|
+
const whereClause = identifiers
|
|
96
|
+
.map((id) => `\`${id}\` = ?`)
|
|
97
|
+
.join(" AND ");
|
|
98
|
+
rows.push(`WHEN (${whereClause}) THEN ?`);
|
|
99
|
+
bindings.push(...identifiers.map((i) => row[i]), row[key]);
|
|
90
100
|
}
|
|
91
101
|
}
|
|
92
102
|
|
|
@@ -94,13 +104,18 @@ function generateBatchUpdateSQL<Id extends string>(
|
|
|
94
104
|
cases.push(`\`${key}\` = CASE ${whenThen} ELSE \`${key}\` END`);
|
|
95
105
|
}
|
|
96
106
|
|
|
97
|
-
const
|
|
98
|
-
|
|
107
|
+
const whereInClauses = identifiers
|
|
108
|
+
.map((col) => `${col} IN (${data.map(() => "?").join(", ")})`)
|
|
109
|
+
.join(" AND ");
|
|
110
|
+
|
|
111
|
+
const whereInBindings = identifiers.flatMap((col) =>
|
|
112
|
+
data.map((row) => row[col])
|
|
113
|
+
);
|
|
114
|
+
|
|
99
115
|
const sql = knex.raw(
|
|
100
|
-
`UPDATE \`${tableName}\` SET ${cases.join(
|
|
101
|
-
|
|
102
|
-
)} WHERE ${identifier} IN (${whereInPlaceholders})`,
|
|
103
|
-
[...bindings, ...whereInIds]
|
|
116
|
+
`UPDATE \`${tableName}\` SET ${cases.join(", ")} WHERE ${whereInClauses}`,
|
|
117
|
+
[...bindings, ...whereInBindings]
|
|
104
118
|
);
|
|
105
|
-
|
|
119
|
+
|
|
120
|
+
return sql.toQuery();
|
|
106
121
|
}
|
|
@@ -144,17 +144,26 @@ export class UpsertBuilder {
|
|
|
144
144
|
};
|
|
145
145
|
}
|
|
146
146
|
|
|
147
|
-
async upsert(
|
|
148
|
-
|
|
147
|
+
async upsert(
|
|
148
|
+
wdb: Knex,
|
|
149
|
+
tableName: string,
|
|
150
|
+
chunkSize?: number
|
|
151
|
+
): Promise<number[]> {
|
|
152
|
+
return this.upsertOrInsert(wdb, tableName, "upsert", chunkSize);
|
|
149
153
|
}
|
|
150
|
-
async insertOnly(
|
|
151
|
-
|
|
154
|
+
async insertOnly(
|
|
155
|
+
wdb: Knex,
|
|
156
|
+
tableName: string,
|
|
157
|
+
chunkSize?: number
|
|
158
|
+
): Promise<number[]> {
|
|
159
|
+
return this.upsertOrInsert(wdb, tableName, "insert", chunkSize);
|
|
152
160
|
}
|
|
153
161
|
|
|
154
162
|
async upsertOrInsert(
|
|
155
163
|
wdb: Knex,
|
|
156
164
|
tableName: string,
|
|
157
|
-
mode: "upsert" | "insert"
|
|
165
|
+
mode: "upsert" | "insert",
|
|
166
|
+
chunkSize?: number
|
|
158
167
|
): Promise<number[]> {
|
|
159
168
|
if (this.hasTable(tableName) === false) {
|
|
160
169
|
return [];
|
|
@@ -177,22 +186,6 @@ export class UpsertBuilder {
|
|
|
177
186
|
throw new Error(`${tableName} 해결되지 않은 참조가 있습니다.`);
|
|
178
187
|
}
|
|
179
188
|
|
|
180
|
-
// 내부 참조 있는 경우 필터하여 분리
|
|
181
|
-
const groups = _.groupBy(table.rows, (row) =>
|
|
182
|
-
Object.entries(row).some(([, value]) => isRefField(value))
|
|
183
|
-
? "selfRef"
|
|
184
|
-
: "normal"
|
|
185
|
-
);
|
|
186
|
-
const targetRows = groups.normal;
|
|
187
|
-
|
|
188
|
-
// Insert On Duplicate Update
|
|
189
|
-
const q = wdb.insert(targetRows).into(tableName);
|
|
190
|
-
if (mode === "insert") {
|
|
191
|
-
await q;
|
|
192
|
-
} else if (mode === "upsert") {
|
|
193
|
-
await q.onDuplicateUpdate.apply(q, Object.keys(targetRows[0]));
|
|
194
|
-
}
|
|
195
|
-
|
|
196
189
|
// 전체 테이블 순회하여 현재 테이블 참조하는 모든 테이블 추출
|
|
197
190
|
const { references, refTables } = Array.from(this.tables).reduce(
|
|
198
191
|
(r, [, table]) => {
|
|
@@ -211,19 +204,39 @@ export class UpsertBuilder {
|
|
|
211
204
|
refTables: [] as TableData[],
|
|
212
205
|
}
|
|
213
206
|
);
|
|
214
|
-
|
|
215
207
|
const extractFields = _.uniq(references).map(
|
|
216
208
|
(reference) => reference.split(".")[1]
|
|
217
209
|
);
|
|
218
210
|
|
|
219
|
-
//
|
|
220
|
-
const
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
const uuidMap = new Map<string, any>(
|
|
225
|
-
upsertedRows.map((row: any) => [row.uuid, row])
|
|
211
|
+
// 내부 참조 있는 경우 필터하여 분리
|
|
212
|
+
const groups = _.groupBy(table.rows, (row) =>
|
|
213
|
+
Object.entries(row).some(([, value]) => isRefField(value))
|
|
214
|
+
? "selfRef"
|
|
215
|
+
: "normal"
|
|
226
216
|
);
|
|
217
|
+
const normalRows = groups.normal ?? [];
|
|
218
|
+
const selfRefRows = groups.selfRef ?? [];
|
|
219
|
+
|
|
220
|
+
const chunks = chunkSize ? _.chunk(normalRows, chunkSize) : [normalRows];
|
|
221
|
+
const uuidMap = new Map<string, any>();
|
|
222
|
+
|
|
223
|
+
for (const chunk of chunks) {
|
|
224
|
+
const q = wdb.insert(chunk).into(tableName);
|
|
225
|
+
if (mode === "insert") {
|
|
226
|
+
await q;
|
|
227
|
+
} else if (mode === "upsert") {
|
|
228
|
+
await q.onDuplicateUpdate.apply(q, Object.keys(normalRows[0]));
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// upsert된 row들을 다시 조회하여 uuidMap에 저장
|
|
232
|
+
const uuids = chunk.map((row) => row.uuid);
|
|
233
|
+
const upsertedRows = await wdb(tableName)
|
|
234
|
+
.select(_.uniq(["uuid", "id", ...extractFields]))
|
|
235
|
+
.whereIn("uuid", uuids);
|
|
236
|
+
upsertedRows.forEach((row: any) => {
|
|
237
|
+
uuidMap.set(row.uuid, row);
|
|
238
|
+
});
|
|
239
|
+
}
|
|
227
240
|
|
|
228
241
|
// 해당 테이블 참조를 실제 밸류로 변경
|
|
229
242
|
refTables.map((table) => {
|
|
@@ -245,14 +258,17 @@ export class UpsertBuilder {
|
|
|
245
258
|
});
|
|
246
259
|
});
|
|
247
260
|
|
|
248
|
-
const
|
|
261
|
+
const allIds = Array.from(uuidMap.values()).map((row) => row.id);
|
|
249
262
|
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
263
|
+
// 자기 참조가 있는 경우 재귀적으로 upsert
|
|
264
|
+
if (selfRefRows.length > 0) {
|
|
265
|
+
// 처리된 데이터를 제외하고 다시 upsert
|
|
266
|
+
table.rows = selfRefRows;
|
|
267
|
+
const selfRefIds = await this.upsert(wdb, tableName, chunkSize);
|
|
268
|
+
allIds.push(...selfRefIds);
|
|
253
269
|
}
|
|
254
270
|
|
|
255
|
-
return
|
|
271
|
+
return allIds;
|
|
256
272
|
}
|
|
257
273
|
|
|
258
274
|
async updateBatch(
|
|
@@ -260,7 +276,7 @@ export class UpsertBuilder {
|
|
|
260
276
|
tableName: string,
|
|
261
277
|
options?: {
|
|
262
278
|
chunkSize?: number;
|
|
263
|
-
where?: string;
|
|
279
|
+
where?: string | string[];
|
|
264
280
|
}
|
|
265
281
|
): Promise<void> {
|
|
266
282
|
options = _.defaults(options, {
|
|
@@ -276,16 +292,14 @@ export class UpsertBuilder {
|
|
|
276
292
|
return;
|
|
277
293
|
}
|
|
278
294
|
|
|
295
|
+
const whereColumns = Array.isArray(options.where)
|
|
296
|
+
? options.where
|
|
297
|
+
: [options.where ?? "id"];
|
|
279
298
|
const rows = table.rows.map((_row) => {
|
|
280
299
|
const { uuid, ...row } = _row;
|
|
281
300
|
return row as RowWithId<string>;
|
|
282
301
|
});
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
tableName,
|
|
286
|
-
options.where ?? "id",
|
|
287
|
-
rows,
|
|
288
|
-
options.chunkSize
|
|
289
|
-
);
|
|
302
|
+
|
|
303
|
+
await batchUpdate(wdb, tableName, whereColumns, rows, options.chunkSize);
|
|
290
304
|
}
|
|
291
305
|
}
|