sonamu 0.7.11 → 0.7.13
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/config.d.ts +10 -6
- package/dist/api/config.d.ts.map +1 -1
- package/dist/api/config.js +2 -1
- package/dist/api/sonamu.d.ts +4 -0
- package/dist/api/sonamu.d.ts.map +1 -1
- package/dist/api/sonamu.js +49 -5
- package/dist/bin/cli.js +118 -170
- package/dist/database/base-model.d.ts +10 -50
- package/dist/database/base-model.d.ts.map +1 -1
- package/dist/database/base-model.js +19 -84
- package/dist/database/base-model.types.d.ts +4 -4
- package/dist/database/base-model.types.d.ts.map +1 -1
- package/dist/database/base-model.types.js +1 -1
- package/dist/database/db.d.ts +1 -0
- package/dist/database/db.d.ts.map +1 -1
- package/dist/database/db.js +24 -13
- package/dist/database/puri-subset.test-d.js +1 -1
- package/dist/database/puri-subset.types.d.ts +1 -0
- package/dist/database/puri-subset.types.d.ts.map +1 -1
- package/dist/database/puri-subset.types.js +2 -2
- package/dist/database/puri.d.ts +82 -3
- package/dist/database/puri.d.ts.map +1 -1
- package/dist/database/puri.js +180 -14
- package/dist/database/puri.types.d.ts +33 -6
- package/dist/database/puri.types.d.ts.map +1 -1
- package/dist/database/puri.types.js +1 -1
- package/dist/database/puri.types.test-d.js +1 -1
- package/dist/entity/entity-manager.d.ts +5 -4
- package/dist/entity/entity-manager.d.ts.map +1 -1
- package/dist/entity/entity-manager.js +8 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -3
- package/dist/migration/code-generation.d.ts.map +1 -1
- package/dist/migration/code-generation.js +33 -2
- package/dist/migration/postgresql-schema-reader.d.ts.map +1 -1
- package/dist/migration/postgresql-schema-reader.js +53 -22
- package/dist/naite/messaging-types.d.ts.map +1 -1
- package/dist/naite/messaging-types.js +1 -1
- package/dist/naite/naite.js +2 -2
- package/dist/stream/sse.d.ts +2 -6
- package/dist/stream/sse.d.ts.map +1 -1
- package/dist/stream/sse.js +9 -3
- package/dist/syncer/api-parser.d.ts.map +1 -1
- package/dist/syncer/api-parser.js +7 -2
- package/dist/syncer/file-patterns.d.ts +1 -1
- package/dist/syncer/file-patterns.d.ts.map +1 -1
- package/dist/syncer/file-patterns.js +6 -5
- package/dist/syncer/module-loader.d.ts +5 -0
- package/dist/syncer/module-loader.d.ts.map +1 -1
- package/dist/syncer/module-loader.js +17 -1
- package/dist/syncer/syncer.d.ts +5 -1
- package/dist/syncer/syncer.d.ts.map +1 -1
- package/dist/syncer/syncer.js +28 -19
- package/dist/tasks/decorator.d.ts +26 -0
- package/dist/tasks/decorator.d.ts.map +1 -0
- package/dist/tasks/decorator.js +28 -0
- package/dist/tasks/step-wrapper.d.ts +18 -0
- package/dist/tasks/step-wrapper.d.ts.map +1 -0
- package/dist/tasks/step-wrapper.js +38 -0
- package/dist/tasks/workflow-manager.d.ts +40 -0
- package/dist/tasks/workflow-manager.d.ts.map +1 -0
- package/dist/tasks/workflow-manager.js +193 -0
- package/dist/template/implementations/generated.template.d.ts.map +1 -1
- package/dist/template/implementations/generated.template.js +7 -3
- package/dist/types/types.d.ts +26 -10
- package/dist/types/types.d.ts.map +1 -1
- package/dist/types/types.js +15 -2
- package/dist/ui/ai-api.d.ts +1 -0
- package/dist/ui/ai-api.d.ts.map +1 -0
- package/dist/ui/ai-api.js +50 -0
- package/dist/ui/ai-client.d.ts +1 -0
- package/dist/ui/ai-client.d.ts.map +1 -0
- package/dist/ui/ai-client.js +438 -0
- package/dist/ui/api.d.ts +3 -0
- package/dist/ui/api.d.ts.map +1 -0
- package/dist/ui/api.js +680 -0
- package/dist/ui-web/assets/brand-icons-Cu_C0hZ4.svg +1008 -0
- package/dist/ui-web/assets/brand-icons-F3SPCeH1.woff +0 -0
- package/dist/ui-web/assets/brand-icons-XL9sxUpA.woff2 +0 -0
- package/dist/ui-web/assets/brand-icons-sqJ2Pg7a.eot +0 -0
- package/dist/ui-web/assets/brand-icons-ubhWoxly.ttf +0 -0
- package/dist/ui-web/assets/flags-DOLqOU7Y.png +0 -0
- package/dist/ui-web/assets/icons-BOCtAERH.woff +0 -0
- package/dist/ui-web/assets/icons-CHzK1VD9.eot +0 -0
- package/dist/ui-web/assets/icons-D29ZQHHw.ttf +0 -0
- package/dist/ui-web/assets/icons-Du6TOHnR.woff2 +0 -0
- package/dist/ui-web/assets/icons-RwhydX30.svg +1518 -0
- package/dist/ui-web/assets/index-CpaB9P6g.css +1 -0
- package/dist/ui-web/assets/index-J9MCfjCd.js +95 -0
- package/dist/ui-web/assets/outline-icons-BfdLr8tr.svg +366 -0
- package/dist/ui-web/assets/outline-icons-DD8jm0uy.ttf +0 -0
- package/dist/ui-web/assets/outline-icons-DInHoiqI.woff2 +0 -0
- package/dist/ui-web/assets/outline-icons-LX8adJ4n.eot +0 -0
- package/dist/ui-web/assets/outline-icons-aQ88nltS.woff +0 -0
- package/dist/ui-web/assets/provider-utils_false-BKJD46kk.js +1 -0
- package/dist/ui-web/assets/provider-utils_false-Bu5lmX18.js +1 -0
- package/dist/ui-web/index.html +13 -0
- package/dist/ui-web/vite.svg +1 -0
- package/dist/utils/formatter.d.ts.map +1 -1
- package/dist/utils/formatter.js +10 -2
- package/dist/utils/model.d.ts +9 -2
- package/dist/utils/model.d.ts.map +1 -1
- package/dist/utils/model.js +16 -1
- package/dist/utils/type-utils.d.ts.map +1 -1
- package/dist/utils/type-utils.js +3 -1
- package/dist/vector/embedding.d.ts +2 -5
- package/dist/vector/embedding.d.ts.map +1 -1
- package/dist/vector/embedding.js +9 -13
- package/dist/vector/types.d.ts.map +1 -1
- package/dist/vector/types.js +1 -1
- package/package.json +9 -5
- package/src/api/config.ts +15 -11
- package/src/api/sonamu.ts +60 -6
- package/src/bin/cli.ts +57 -119
- package/src/database/base-model.ts +21 -128
- package/src/database/base-model.types.ts +3 -4
- package/src/database/db.ts +28 -18
- package/src/database/puri-subset.test-d.ts +1 -0
- package/src/database/puri-subset.types.ts +2 -0
- package/src/database/puri.ts +238 -27
- package/src/database/puri.types.test-d.ts +1 -1
- package/src/database/puri.types.ts +49 -6
- package/src/entity/entity-manager.ts +9 -0
- package/src/index.ts +1 -1
- package/src/migration/code-generation.ts +40 -1
- package/src/migration/postgresql-schema-reader.ts +53 -22
- package/src/naite/messaging-types.ts +43 -44
- package/src/naite/naite.ts +1 -1
- package/src/shared/app.shared.ts.txt +13 -0
- package/src/shared/web.shared.ts.txt +13 -0
- package/src/stream/sse.ts +15 -3
- package/src/syncer/api-parser.ts +6 -1
- package/src/syncer/file-patterns.ts +11 -9
- package/src/syncer/module-loader.ts +35 -0
- package/src/syncer/syncer.ts +34 -21
- package/src/tasks/decorator.ts +71 -0
- package/src/tasks/step-wrapper.ts +84 -0
- package/src/tasks/workflow-manager.ts +330 -0
- package/src/template/implementations/generated.template.ts +19 -6
- package/src/types/types.ts +20 -4
- package/src/ui/ai-api.ts +60 -0
- package/src/ui/ai-client.ts +499 -0
- package/src/ui/api.ts +786 -0
- package/src/utils/formatter.ts +8 -1
- package/src/utils/model.ts +26 -2
- package/src/utils/type-utils.ts +2 -0
- package/src/vector/embedding.ts +10 -14
- package/src/vector/types.ts +1 -2
- package/dist/vector/vector-search.d.ts +0 -47
- package/dist/vector/vector-search.d.ts.map +0 -1
- package/dist/vector/vector-search.js +0 -176
- package/src/vector/vector-search.ts +0 -261
package/src/bin/cli.ts
CHANGED
|
@@ -22,66 +22,68 @@ import { BUILD_DIR, SWC_BUILD_COMMAND, TSC_TYPE_CHECK_COMMAND } from "./build-co
|
|
|
22
22
|
let migrator: Migrator;
|
|
23
23
|
|
|
24
24
|
async function bootstrap() {
|
|
25
|
-
const notToInit = ["dev", "build", "start"
|
|
25
|
+
const notToInit = ["dev", "build", "start"].includes(process.argv[2] ?? "");
|
|
26
26
|
if (!notToInit) {
|
|
27
27
|
await Sonamu.init(false, false);
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
30
|
+
try {
|
|
31
|
+
await tsicli(process.argv, {
|
|
32
|
+
types: {
|
|
33
|
+
"#entityId": {
|
|
34
|
+
type: "autocomplete",
|
|
35
|
+
name: "#entityId",
|
|
36
|
+
message: "Please input #entityId",
|
|
37
|
+
choices: EntityManager.getAllParentIds().map((entityId) => ({
|
|
38
|
+
title: entityId,
|
|
39
|
+
value: entityId,
|
|
40
|
+
})),
|
|
41
|
+
},
|
|
42
|
+
"#recordIds": "number[]",
|
|
43
|
+
"#name": "string",
|
|
40
44
|
},
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
},
|
|
84
|
-
});
|
|
45
|
+
args: [
|
|
46
|
+
["fixture", "init"],
|
|
47
|
+
["fixture", "import", "#entityId", "#recordIds"],
|
|
48
|
+
["fixture", "sync"],
|
|
49
|
+
["migrate", "run"],
|
|
50
|
+
["migrate", "check"],
|
|
51
|
+
["migrate", "rollback"],
|
|
52
|
+
["migrate", "reset"],
|
|
53
|
+
["migrate", "clear"],
|
|
54
|
+
["migrate", "status"],
|
|
55
|
+
["stub", "practice", "#name"],
|
|
56
|
+
["stub", "entity", "#name"],
|
|
57
|
+
["scaffold", "model", "#entityId"],
|
|
58
|
+
["scaffold", "model_test", "#entityId"],
|
|
59
|
+
["scaffold", "view_list", "#entityId"],
|
|
60
|
+
["scaffold", "view_form", "#entityId"],
|
|
61
|
+
["sync"],
|
|
62
|
+
["dev"],
|
|
63
|
+
["build"],
|
|
64
|
+
["start"],
|
|
65
|
+
],
|
|
66
|
+
runners: {
|
|
67
|
+
migrate_status,
|
|
68
|
+
migrate_run,
|
|
69
|
+
fixture_init,
|
|
70
|
+
fixture_import,
|
|
71
|
+
fixture_sync,
|
|
72
|
+
stub_practice,
|
|
73
|
+
stub_entity,
|
|
74
|
+
scaffold_model,
|
|
75
|
+
scaffold_model_test,
|
|
76
|
+
// scaffold_view_list,
|
|
77
|
+
// scaffold_view_form,
|
|
78
|
+
sync,
|
|
79
|
+
dev,
|
|
80
|
+
build,
|
|
81
|
+
start,
|
|
82
|
+
},
|
|
83
|
+
});
|
|
84
|
+
} finally {
|
|
85
|
+
await Sonamu.destroy();
|
|
86
|
+
}
|
|
85
87
|
}
|
|
86
88
|
|
|
87
89
|
bootstrap().finally(async () => {
|
|
@@ -448,67 +450,3 @@ async function scaffold_model_test(entityId: string) {
|
|
|
448
450
|
entityId,
|
|
449
451
|
});
|
|
450
452
|
}
|
|
451
|
-
|
|
452
|
-
async function ui() {
|
|
453
|
-
try {
|
|
454
|
-
const apiRootPath = findApiRootPath();
|
|
455
|
-
|
|
456
|
-
// 사용자 프로젝트의 패키지들 중에서 @sonamu-kit/ui를 찾습니다.
|
|
457
|
-
// 이를 위해서 createRequire를 사용하여 프로젝트 경로 기준으로 resolve합니다.
|
|
458
|
-
const projectRequire = createRequire(path.join(apiRootPath, "package.json"));
|
|
459
|
-
const uiPackagePath = projectRequire.resolve("@sonamu-kit/ui"); // 없으면 여기서 터져요(MODULE_NOT_FOUND)
|
|
460
|
-
const uiNodePath = path.join(path.dirname(uiPackagePath), "run-ui.js");
|
|
461
|
-
|
|
462
|
-
if (!(await exists(uiNodePath))) {
|
|
463
|
-
console.log(
|
|
464
|
-
chalk.red(`UI runner script not found at ${uiNodePath}. Please rebuild @sonamu-kit/ui.`),
|
|
465
|
-
);
|
|
466
|
-
return;
|
|
467
|
-
}
|
|
468
|
-
|
|
469
|
-
// UI를 별도 프로세스로 실행 (hmr-hook 활성화)
|
|
470
|
-
const uiProcess = spawn(
|
|
471
|
-
process.execPath,
|
|
472
|
-
[
|
|
473
|
-
"--import",
|
|
474
|
-
"sonamu/ts-loader-register",
|
|
475
|
-
"--import",
|
|
476
|
-
"sonamu/hmr-hook-register",
|
|
477
|
-
"--enable-source-maps",
|
|
478
|
-
"--no-warnings",
|
|
479
|
-
uiNodePath,
|
|
480
|
-
],
|
|
481
|
-
{
|
|
482
|
-
stdio: "inherit",
|
|
483
|
-
env: {
|
|
484
|
-
...process.env,
|
|
485
|
-
HOT: "yes",
|
|
486
|
-
API_ROOT_PATH: apiRootPath, // UI는 얘만 알면 돼요! 나머지는 얘가 떠서 알아서 할 것임 ㅎ
|
|
487
|
-
},
|
|
488
|
-
},
|
|
489
|
-
);
|
|
490
|
-
|
|
491
|
-
// 종료 처리
|
|
492
|
-
const cleanup = () => {
|
|
493
|
-
console.log(chalk.yellow("\n\n👋 Shutting down UI server..."));
|
|
494
|
-
uiProcess.kill("SIGTERM");
|
|
495
|
-
process.exit(0);
|
|
496
|
-
};
|
|
497
|
-
|
|
498
|
-
process.on("SIGINT", cleanup);
|
|
499
|
-
process.on("SIGTERM", cleanup);
|
|
500
|
-
|
|
501
|
-
uiProcess.on("exit", (code) => {
|
|
502
|
-
if (code !== 0) {
|
|
503
|
-
console.error(chalk.red(`❌ UI server exited with code ${code}`));
|
|
504
|
-
process.exit(code || 1);
|
|
505
|
-
}
|
|
506
|
-
});
|
|
507
|
-
} catch (e: unknown) {
|
|
508
|
-
if (e instanceof Error && e.message.includes("isn't declared")) {
|
|
509
|
-
console.log(`You need to install ${chalk.blue(`@sonamu-kit/ui`)} first.`);
|
|
510
|
-
return;
|
|
511
|
-
}
|
|
512
|
-
throw e;
|
|
513
|
-
}
|
|
514
|
-
}
|
|
@@ -2,24 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
import type { Knex } from "knex";
|
|
4
4
|
import { group, isObject, omit, set } from "radashi";
|
|
5
|
+
import type { ListResult } from "..";
|
|
5
6
|
import { Sonamu } from "../api";
|
|
6
|
-
import {
|
|
7
|
-
import type { DatabaseSchemaExtend } from "../types/types";
|
|
7
|
+
import type { DatabaseSchemaExtend, SonamuQueryMode } from "../types/types";
|
|
8
8
|
import { getJoinTables, getTableNamesFromWhere } from "../utils/sql-parser";
|
|
9
9
|
import { chunk } from "../utils/utils";
|
|
10
|
-
import type {
|
|
11
|
-
EmbeddingItem,
|
|
12
|
-
EmbeddingProvider,
|
|
13
|
-
HybridSearchOptions,
|
|
14
|
-
HybridSearchResult,
|
|
15
|
-
ProgressCallback,
|
|
16
|
-
VectorSearchOptions,
|
|
17
|
-
VectorSearchResult,
|
|
18
|
-
} from "../vector/types";
|
|
19
|
-
import { VectorSearch } from "../vector/vector-search";
|
|
20
10
|
import type {
|
|
21
11
|
EnhancerMap,
|
|
22
|
-
ExecuteSubsetQueryResult,
|
|
23
12
|
ResolveSubsetIntersection,
|
|
24
13
|
UnionExtractedTTables,
|
|
25
14
|
} from "./base-model.types";
|
|
@@ -69,118 +58,7 @@ export class BaseModelClass<
|
|
|
69
58
|
return new PuriWrapper(db, new UpsertBuilder());
|
|
70
59
|
}
|
|
71
60
|
|
|
72
|
-
// VectorSearch 인스턴스 캐시
|
|
73
|
-
private _vectorSearch: VectorSearch<any> | null = null;
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* 벡터 검색 인스턴스 반환
|
|
77
|
-
* - 기본 provider: voyage
|
|
78
|
-
* - 기본 dimensions: 1024 (DEFAULT_VECTOR_CONFIG 사용)
|
|
79
|
-
*/
|
|
80
|
-
getVector<T = Record<string, unknown>>(): VectorSearch<T> {
|
|
81
|
-
if (this._vectorSearch) {
|
|
82
|
-
return this._vectorSearch as VectorSearch<T>;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
const entity = EntityManager.get(this.modelName);
|
|
86
|
-
|
|
87
|
-
this._vectorSearch = new VectorSearch<T>(this.getDB("w"), entity.table);
|
|
88
|
-
|
|
89
|
-
return this._vectorSearch as VectorSearch<T>;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
/**
|
|
93
|
-
* 벡터 검색 (코사인 유사도)
|
|
94
|
-
* @param query - 검색어
|
|
95
|
-
* @param options - 검색 옵션
|
|
96
|
-
*/
|
|
97
|
-
async vectorSearch<T = Record<string, unknown>>(
|
|
98
|
-
query: string,
|
|
99
|
-
options: VectorSearchOptions & { provider?: EmbeddingProvider } = {},
|
|
100
|
-
): Promise<VectorSearchResult<T>[]> {
|
|
101
|
-
const entity = EntityManager.get(this.modelName);
|
|
102
|
-
const vectorProp = entity.getVectorColumn();
|
|
103
|
-
if (!vectorProp) {
|
|
104
|
-
throw new Error(`${this.modelName} Entity에 vector 컬럼이 정의되지 않았습니다.`);
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
const vs = new VectorSearch<T>(this.getDB("w"), entity.table);
|
|
108
|
-
return vs.search(query, options.provider ?? "voyage", {
|
|
109
|
-
...options,
|
|
110
|
-
embeddingColumn: options.embeddingColumn ?? vectorProp.name,
|
|
111
|
-
});
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
/**
|
|
115
|
-
* 하이브리드 검색 (Vector + FTS)
|
|
116
|
-
* @param query - 검색어
|
|
117
|
-
* @param options - 검색 옵션
|
|
118
|
-
*/
|
|
119
|
-
async hybridSearch<T = Record<string, unknown>>(
|
|
120
|
-
query: string,
|
|
121
|
-
options: HybridSearchOptions & { provider?: EmbeddingProvider } = {},
|
|
122
|
-
): Promise<HybridSearchResult<T>[]> {
|
|
123
|
-
const entity = EntityManager.get(this.modelName);
|
|
124
|
-
const vectorProp = entity.getVectorColumn();
|
|
125
|
-
if (!vectorProp) {
|
|
126
|
-
throw new Error(`${this.modelName} Entity에 vector 컬럼이 정의되지 않았습니다.`);
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
const vs = new VectorSearch<T>(this.getDB("w"), entity.table);
|
|
130
|
-
return vs.hybridSearch(query, options.provider ?? "voyage", {
|
|
131
|
-
...options,
|
|
132
|
-
embeddingColumn: options.embeddingColumn ?? vectorProp.name,
|
|
133
|
-
});
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
/**
|
|
137
|
-
* 단일 레코드에 임베딩 저장
|
|
138
|
-
* @param id - 레코드 ID
|
|
139
|
-
* @param text - 임베딩할 텍스트
|
|
140
|
-
* @param options - provider, embeddingColumn 옵션
|
|
141
|
-
*/
|
|
142
|
-
async saveEmbedding(
|
|
143
|
-
id: number,
|
|
144
|
-
text: string,
|
|
145
|
-
options: { provider?: EmbeddingProvider; embeddingColumn?: string } = {},
|
|
146
|
-
): Promise<void> {
|
|
147
|
-
const entity = EntityManager.get(this.modelName);
|
|
148
|
-
const vectorProp = entity.getVectorColumn(options.embeddingColumn);
|
|
149
|
-
if (!vectorProp) {
|
|
150
|
-
throw new Error(`${this.modelName} Entity에 vector 컬럼이 정의되지 않았습니다.`);
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
const { provider = "voyage" } = options;
|
|
154
|
-
const vs = this.getVector();
|
|
155
|
-
return vs.saveEmbedding(id, text, provider, vectorProp.name);
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
/**
|
|
159
|
-
* 여러 레코드에 임베딩 일괄 저장
|
|
160
|
-
* @param items - { id, text } 배열
|
|
161
|
-
* @param options - provider, embeddingColumn, onProgress 옵션
|
|
162
|
-
*/
|
|
163
|
-
async saveEmbeddingsBatch(
|
|
164
|
-
items: EmbeddingItem[],
|
|
165
|
-
options: {
|
|
166
|
-
provider?: EmbeddingProvider;
|
|
167
|
-
embeddingColumn?: string;
|
|
168
|
-
onProgress?: ProgressCallback;
|
|
169
|
-
} = {},
|
|
170
|
-
): Promise<void> {
|
|
171
|
-
const entity = EntityManager.get(this.modelName);
|
|
172
|
-
const vectorProp = entity.getVectorColumn(options.embeddingColumn);
|
|
173
|
-
if (!vectorProp) {
|
|
174
|
-
throw new Error(`${this.modelName} Entity에 vector 컬럼이 정의되지 않았습니다.`);
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
const { provider = "voyage", onProgress } = options;
|
|
178
|
-
const vs = this.getVector();
|
|
179
|
-
return vs.saveEmbeddingsBatch(items, provider, vectorProp.name, onProgress);
|
|
180
|
-
}
|
|
181
|
-
|
|
182
61
|
async destroy() {
|
|
183
|
-
this._vectorSearch = null;
|
|
184
62
|
return DB.destroy();
|
|
185
63
|
}
|
|
186
64
|
|
|
@@ -275,6 +153,11 @@ export class BaseModelClass<
|
|
|
275
153
|
async executeSubsetQuery<
|
|
276
154
|
T extends TSubsetKey,
|
|
277
155
|
TComputedResults extends InferAllSubsets<TSubsetQueries, TLoaderQueries>,
|
|
156
|
+
LP extends {
|
|
157
|
+
num?: number;
|
|
158
|
+
page?: number;
|
|
159
|
+
queryMode?: SonamuQueryMode;
|
|
160
|
+
},
|
|
278
161
|
>(
|
|
279
162
|
params: {
|
|
280
163
|
subset: T;
|
|
@@ -282,12 +165,12 @@ export class BaseModelClass<
|
|
|
282
165
|
params: {
|
|
283
166
|
num: number;
|
|
284
167
|
page: number;
|
|
285
|
-
queryMode?:
|
|
168
|
+
queryMode?: SonamuQueryMode;
|
|
286
169
|
};
|
|
287
170
|
debug?: boolean;
|
|
288
171
|
optimizeCountQuery?: boolean;
|
|
289
172
|
} & EnhancerParam<TSubsetKey, TComputedResults, TSubsetMapping>,
|
|
290
|
-
): Promise<
|
|
173
|
+
): Promise<ListResult<LP, TSubsetMapping[T]>> {
|
|
291
174
|
const { subset, qb, params: queryParams, debug = false, optimizeCountQuery = false } = params;
|
|
292
175
|
|
|
293
176
|
if (!this.loaderQueries) {
|
|
@@ -296,9 +179,13 @@ export class BaseModelClass<
|
|
|
296
179
|
|
|
297
180
|
const { num, page } = queryParams;
|
|
298
181
|
|
|
299
|
-
// COUNT 쿼리 실행
|
|
182
|
+
// COUNT 쿼리 실행 (queryMode: list일 때는 0 리턴)
|
|
300
183
|
const total = await this.executeCountQuery(qb, queryParams, debug, optimizeCountQuery);
|
|
301
184
|
|
|
185
|
+
if (queryParams?.queryMode === "count") {
|
|
186
|
+
return { total } as ListResult<LP, TSubsetMapping[T]>;
|
|
187
|
+
}
|
|
188
|
+
|
|
302
189
|
// LIST 쿼리 실행
|
|
303
190
|
const computedRows = await this.executeListQuery(subset, qb, queryParams, num, page, debug);
|
|
304
191
|
|
|
@@ -308,7 +195,13 @@ export class BaseModelClass<
|
|
|
308
195
|
computedRows.map((row) => enhancer?.(row) ?? row),
|
|
309
196
|
)) as TSubsetMapping[T][];
|
|
310
197
|
|
|
311
|
-
|
|
198
|
+
if (queryParams.queryMode === "list") {
|
|
199
|
+
// 리스트만 리턴
|
|
200
|
+
return { rows } as ListResult<LP, TSubsetMapping[T]>;
|
|
201
|
+
} else {
|
|
202
|
+
// 둘다 리턴
|
|
203
|
+
return { rows, total } as ListResult<LP, TSubsetMapping[T]>;
|
|
204
|
+
}
|
|
312
205
|
}
|
|
313
206
|
|
|
314
207
|
/**
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
* Enhancer, SubsetQuery 교집합 등 Model 계층에서 필요한 타입 정의.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
+
import type { ListResult } from "..";
|
|
10
11
|
import type { DatabaseSchemaExtend } from "../types/types";
|
|
11
12
|
import type { Puri } from "./puri";
|
|
12
13
|
import type { PuriSubsetFn } from "./puri-subset.types";
|
|
@@ -151,7 +152,5 @@ export type ExecuteSubsetQueryParams<
|
|
|
151
152
|
export type ExecuteSubsetQueryResult<
|
|
152
153
|
TSubsetMapping extends Record<string, any>,
|
|
153
154
|
T extends string,
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
total: number;
|
|
157
|
-
};
|
|
155
|
+
LP extends { queryMode?: "list" | "count" | "both" },
|
|
156
|
+
> = ListResult<LP, TSubsetMapping[T]>;
|
package/src/database/db.ts
CHANGED
|
@@ -56,30 +56,40 @@ export class DBClass {
|
|
|
56
56
|
const instanceName = which === "w" ? "wdb" : "rdb";
|
|
57
57
|
|
|
58
58
|
if (!this[instanceName]) {
|
|
59
|
-
|
|
60
|
-
switch (process.env.NODE_ENV ?? "development") {
|
|
61
|
-
case "development":
|
|
62
|
-
case "staging":
|
|
63
|
-
config =
|
|
64
|
-
which === "w"
|
|
65
|
-
? dbConfig.development_master
|
|
66
|
-
: (dbConfig.development_slave ?? dbConfig.development_master);
|
|
67
|
-
break;
|
|
68
|
-
case "production":
|
|
69
|
-
config =
|
|
70
|
-
which === "w"
|
|
71
|
-
? dbConfig.production_master
|
|
72
|
-
: (dbConfig.production_slave ?? dbConfig.production_master);
|
|
73
|
-
break;
|
|
74
|
-
default:
|
|
75
|
-
throw new Error(`현재 ENV ${process.env.NODE_ENV}에는 설정 가능한 DB설정이 없습니다.`);
|
|
76
|
-
}
|
|
59
|
+
const config = this.getDBConfig(which);
|
|
77
60
|
this[instanceName] = knex(config);
|
|
78
61
|
}
|
|
79
62
|
|
|
80
63
|
return this[instanceName];
|
|
81
64
|
}
|
|
82
65
|
|
|
66
|
+
getDBConfig(which: DBPreset): Knex.Config {
|
|
67
|
+
const dbConfig = Sonamu.dbConfig;
|
|
68
|
+
if (process.env.NODE_ENV === "test") {
|
|
69
|
+
return {
|
|
70
|
+
...dbConfig.test,
|
|
71
|
+
// 단일 풀
|
|
72
|
+
pool: {
|
|
73
|
+
min: 1,
|
|
74
|
+
max: 1,
|
|
75
|
+
},
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
switch (process.env.NODE_ENV ?? "development") {
|
|
79
|
+
case "development":
|
|
80
|
+
case "staging":
|
|
81
|
+
return which === "w"
|
|
82
|
+
? dbConfig.development_master
|
|
83
|
+
: (dbConfig.development_slave ?? dbConfig.development_master);
|
|
84
|
+
case "production":
|
|
85
|
+
return which === "w"
|
|
86
|
+
? dbConfig.production_master
|
|
87
|
+
: (dbConfig.production_slave ?? dbConfig.production_master);
|
|
88
|
+
default:
|
|
89
|
+
throw new Error(`현재 ENV ${process.env.NODE_ENV}에는 설정 가능한 DB설정이 없습니다.`);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
83
93
|
async destroy(): Promise<void> {
|
|
84
94
|
if (this.wdb !== undefined) {
|
|
85
95
|
await this.wdb.destroy();
|
|
@@ -4,6 +4,7 @@ import type { Puri } from "./puri";
|
|
|
4
4
|
import type { Hydrate, InferAllSubsets, LoadersResult } from "./puri-subset.types";
|
|
5
5
|
import type { PuriWrapper } from "./puri-wrapper";
|
|
6
6
|
|
|
7
|
+
/** biome-ignore lint/suspicious/noExplicitAny: Puri Subset 타입 시스템에서 any를 허용함 */
|
|
7
8
|
type MockPuri<T> = Puri<DatabaseSchemaExtend, any, T>;
|
|
8
9
|
|
|
9
10
|
describe("Hydrate", () => {
|