@simplysm/sd-claude 14.0.88 → 14.0.90
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/claude/references/sd-simplysm14/README.md +16 -17
- package/claude/references/sd-simplysm14/apis/angular/README.md +39 -43
- package/claude/references/sd-simplysm14/apis/angular/controls.md +174 -80
- package/claude/references/sd-simplysm14/apis/angular/crud.md +41 -50
- package/claude/references/sd-simplysm14/apis/angular/directives.md +60 -26
- package/claude/references/sd-simplysm14/apis/angular/features.md +109 -37
- package/claude/references/sd-simplysm14/apis/angular/infra.md +61 -44
- package/claude/references/sd-simplysm14/apis/angular/layout.md +39 -31
- package/claude/references/sd-simplysm14/apis/angular/overlay.md +73 -85
- package/claude/references/sd-simplysm14/apis/angular/routing-appstructure.md +54 -39
- package/claude/references/sd-simplysm14/apis/angular/selection-managers.md +55 -30
- package/claude/references/sd-simplysm14/apis/angular/shared-data.md +71 -67
- package/claude/references/sd-simplysm14/apis/angular/sheet.md +82 -72
- package/claude/references/sd-simplysm14/apis/capacitor-plugin-auto-update/README.md +35 -36
- package/claude/references/sd-simplysm14/apis/capacitor-plugin-file-system/README.md +71 -43
- package/claude/references/sd-simplysm14/apis/capacitor-plugin-intent/README.md +38 -30
- package/claude/references/sd-simplysm14/apis/capacitor-plugin-usb-storage/README.md +45 -50
- package/claude/references/sd-simplysm14/apis/core-browser/README.md +42 -55
- package/claude/references/sd-simplysm14/apis/core-browser/dom-element.md +62 -0
- package/claude/references/sd-simplysm14/apis/core-browser/indexed-db.md +39 -38
- package/claude/references/sd-simplysm14/apis/core-common/README.md +95 -103
- package/claude/references/sd-simplysm14/apis/core-common/array-ext.md +59 -54
- package/claude/references/sd-simplysm14/apis/core-common/async-runtime.md +86 -0
- package/claude/references/sd-simplysm14/apis/core-common/datetime.md +57 -66
- package/claude/references/sd-simplysm14/apis/core-common/errors.md +86 -0
- package/claude/references/sd-simplysm14/apis/core-common/obj.md +60 -42
- package/claude/references/sd-simplysm14/apis/core-common/serialization.md +55 -0
- package/claude/references/sd-simplysm14/apis/core-node/README.md +10 -8
- package/claude/references/sd-simplysm14/apis/core-node/consola.md +29 -32
- package/claude/references/sd-simplysm14/apis/core-node/cpx.md +34 -22
- package/claude/references/sd-simplysm14/apis/core-node/fs-watcher.md +29 -25
- package/claude/references/sd-simplysm14/apis/core-node/fsx.md +40 -53
- package/claude/references/sd-simplysm14/apis/core-node/pathx.md +22 -29
- package/claude/references/sd-simplysm14/apis/core-node/worker.md +31 -31
- package/claude/references/sd-simplysm14/apis/excel/README.md +26 -26
- package/claude/references/sd-simplysm14/apis/excel/cell.md +37 -29
- package/claude/references/sd-simplysm14/apis/excel/conditional-format.md +29 -15
- package/claude/references/sd-simplysm14/apis/excel/style.md +33 -27
- package/claude/references/sd-simplysm14/apis/excel/utils.md +29 -19
- package/claude/references/sd-simplysm14/apis/excel/workbook-worksheet.md +78 -55
- package/claude/references/sd-simplysm14/apis/excel/wrapper.md +42 -45
- package/claude/references/sd-simplysm14/apis/lint/README.md +27 -21
- package/claude/references/sd-simplysm14/apis/lint/rules.md +89 -49
- package/claude/references/sd-simplysm14/apis/orm-common/README.md +6 -62
- package/claude/references/sd-simplysm14/apis/orm-common/db-context.md +149 -67
- package/claude/references/sd-simplysm14/apis/orm-common/expr.md +111 -99
- package/claude/references/sd-simplysm14/apis/orm-common/queryable.md +115 -72
- package/claude/references/sd-simplysm14/apis/orm-common/schema.md +134 -92
- package/claude/references/sd-simplysm14/apis/orm-common/types.md +67 -52
- package/claude/references/sd-simplysm14/apis/orm-node/README.md +63 -26
- package/claude/references/sd-simplysm14/apis/orm-node/db-conn.md +51 -40
- package/claude/references/sd-simplysm14/apis/sd-cli/README.md +10 -12
- package/claude/references/sd-simplysm14/apis/sd-cli/SdTsCompiler.md +92 -45
- package/claude/references/sd-simplysm14/apis/sd-cli/sd-config-types.md +226 -108
- package/claude/references/sd-simplysm14/apis/service-client/README.md +90 -88
- package/claude/references/sd-simplysm14/apis/service-client/orm.md +37 -29
- package/claude/references/sd-simplysm14/apis/service-client/transport.md +45 -20
- package/claude/references/sd-simplysm14/apis/service-common/README.md +89 -40
- package/claude/references/sd-simplysm14/apis/service-common/app-structure.md +126 -34
- package/claude/references/sd-simplysm14/apis/service-common/protocol.md +109 -54
- package/claude/references/sd-simplysm14/apis/service-server/README.md +70 -66
- package/claude/references/sd-simplysm14/apis/service-server/service-authoring.md +47 -47
- package/claude/references/sd-simplysm14/apis/service-server/transport-internals.md +71 -34
- package/claude/references/sd-simplysm14/apis/service-server/v1-legacy.md +31 -32
- package/claude/references/sd-simplysm14/apis/storage/README.md +34 -28
- package/claude/references/sd-simplysm14/manuals/client-app-structure.md +142 -140
- package/claude/references/sd-simplysm14/manuals/client-orm.md +1 -1
- package/claude/references/sd-simplysm14/manuals/client-service.md +19 -7
- package/claude/references/sd-simplysm14/manuals/client-shared-data.md +2 -2
- package/claude/references/sd-simplysm14/manuals/client-system-log.md +11 -3
- package/claude/references/sd-simplysm14/manuals/data-log.md +0 -1
- package/claude/references/sd-simplysm14/manuals/orm.md +16 -0
- package/claude/rules/sd-design-rules.md +10 -0
- package/claude/skills/sd-docs/SKILL.md +58 -46
- package/claude/skills/sd-docs/references/{doc-rules.md → subagent-prompt.md} +103 -103
- package/claude/skills/sd-impl/SKILL.md +1 -1
- package/claude/skills/sd-spec/SKILL.md +858 -858
- package/claude/skills/sd-spec/references/example-spec.md +26 -36
- package/package.json +1 -1
- package/claude/references/sd-simplysm14/apis/core-common/json-transfer.md +0 -47
- package/claude/references/sd-simplysm14/apis/orm-common/query-builder.md +0 -29
- package/claude/skills/sd-demo/evals/fixtures/inventory-list/.specs/inventory/spec.md +0 -99
- package/claude/skills/sd-demo/evals/fixtures/inventory-list/packages/demo-client/package.json +0 -12
- package/claude/skills/sd-demo/evals/fixtures/inventory-list/packages/demo-client/src/index.ts +0 -3
- package/claude/skills/sd-demo/evals/fixtures/inventory-list/packages/demo-client/src/screens/inbound/inbound.list.ts +0 -150
- package/claude/skills/sd-demo/evals/fixtures/inventory-list/packages/demo-client/src/screens/inventory/inventory-master.list.ts +0 -143
- package/claude/skills/sd-demo/evals/fixtures/inventory-list/packages/demo-client/src/screens/outbound/outbound.list.ts +0 -150
- package/claude/skills/sd-demo/evals/fixtures/inventory-list/pnpm-workspace.yaml +0 -2
- package/claude/skills/sd-demo/evals/fixtures/inventory-list/sd.config.ts +0 -12
- package/claude/skills/sd-demo/evals/golden.jsonl +0 -1
- package/claude/skills/sd-dev/evals/fixtures/minimal-ts-pkg/package.json +0 -8
- package/claude/skills/sd-dev/evals/fixtures/minimal-ts-pkg/src/.gitkeep +0 -0
- package/claude/skills/sd-dev/evals/fixtures/minimal-ts-pkg/tests/.gitkeep +0 -0
- package/claude/skills/sd-dev/evals/fixtures/minimal-ts-pkg/tsconfig.json +0 -10
- package/claude/skills/sd-dev/evals/golden.jsonl +0 -1
- package/claude/skills/sd-docs/evals/fixtures/new-write/.claude/references/sd-simplysm14/README.md +0 -7
- package/claude/skills/sd-docs/evals/fixtures/new-write/packages/bar/package.json +0 -5
- package/claude/skills/sd-docs/evals/fixtures/new-write/packages/bar/src/index.ts +0 -3
- package/claude/skills/sd-docs/evals/fixtures/new-write/packages/baz/package.json +0 -6
- package/claude/skills/sd-docs/evals/fixtures/new-write/packages/baz/src/index.ts +0 -1
- package/claude/skills/sd-docs/evals/fixtures/new-write/packages/foo/package.json +0 -5
- package/claude/skills/sd-docs/evals/fixtures/new-write/packages/foo/src/index.ts +0 -8
- package/claude/skills/sd-docs/evals/fixtures/update-mixed/.claude/references/sd-simplysm14/README.md +0 -7
- package/claude/skills/sd-docs/evals/fixtures/update-mixed/.claude/references/sd-simplysm14/apis/foo/README.md +0 -3
- package/claude/skills/sd-docs/evals/fixtures/update-mixed/packages/bar/package.json +0 -5
- package/claude/skills/sd-docs/evals/fixtures/update-mixed/packages/bar/src/index.ts +0 -3
- package/claude/skills/sd-docs/evals/fixtures/update-mixed/packages/baz/package.json +0 -6
- package/claude/skills/sd-docs/evals/fixtures/update-mixed/packages/baz/src/index.ts +0 -1
- package/claude/skills/sd-docs/evals/fixtures/update-mixed/packages/foo/package.json +0 -5
- package/claude/skills/sd-docs/evals/fixtures/update-mixed/packages/foo/src/index.ts +0 -8
- package/claude/skills/sd-docs/evals/golden.jsonl +0 -2
- package/claude/skills/sd-impl/evals/fixtures/case-a-new-screen/.specs/260513120000_warehouse/spec.md +0 -101
- package/claude/skills/sd-impl/evals/fixtures/case-b-update-with-demo/.specs/260513120000_warehouse/spec.md +0 -101
- package/claude/skills/sd-impl/evals/fixtures/case-b-update-with-demo/packages/app/src/screens/box-register/box-register.view.ts +0 -46
- package/claude/skills/sd-impl/evals/fixtures/case-c-new-cross/.specs/260513120000_warehouse/spec.md +0 -89
- package/claude/skills/sd-impl/evals/fixtures/case-d-spec-modify/.specs/260513120000_warehouse/spec.md +0 -101
- package/claude/skills/sd-impl/evals/golden.jsonl +0 -4
- package/claude/skills/sd-manual/evals/fixtures/new-manual/src/notification.ts +0 -25
- package/claude/skills/sd-manual/evals/fixtures/update-manual/.claude/references/sd-simplysm14/manuals/notification.md +0 -14
- package/claude/skills/sd-manual/evals/fixtures/update-manual/src/notification.ts +0 -37
- package/claude/skills/sd-manual/evals/golden.jsonl +0 -2
- package/claude/skills/sd-review/evals/fixtures/code-review/src/foo.ts +0 -7
- package/claude/skills/sd-review/evals/fixtures/doc-review/docs/foo.md +0 -4
- package/claude/skills/sd-review/evals/golden.jsonl +0 -2
- package/claude/skills/sd-skill/evals/fixtures/existing-skill/.claude/skills/todo-format/SKILL.md +0 -14
- package/claude/skills/sd-skill/evals/fixtures/new-skill/.gitkeep +0 -0
- package/claude/skills/sd-skill/evals/golden.jsonl +0 -2
- package/claude/skills/sd-spec/evals/fixtures/case-a-split//355/232/214/354/235/230/353/241/235.md +0 -20
- package/claude/skills/sd-spec/evals/fixtures/case-b-detail/.specs/260513120000_warehouse/spec.md +0 -95
- package/claude/skills/sd-spec/evals/golden.jsonl +0 -2
- package/claude/skills/sd-unpack/evals/fixtures/eml-with-text-attachment/meeting.eml +0 -21
- package/claude/skills/sd-unpack/evals/fixtures/simple-eml/meeting.eml +0 -10
- package/claude/skills/sd-unpack/evals/golden.jsonl +0 -2
- package/claude/skills/sd-use/evals/fixtures/empty/.gitkeep +0 -0
- package/claude/skills/sd-use/evals/golden.jsonl +0 -6
|
@@ -1,111 +1,154 @@
|
|
|
1
|
-
# @simplysm/orm-common — Queryable (쿼리
|
|
1
|
+
# @simplysm/orm-common — Queryable (쿼리 작성·실행 · 프로시저 · 검색)
|
|
2
2
|
|
|
3
|
-
`db.user()`
|
|
3
|
+
`db.user()` 처럼 컨텍스트 멤버를 호출하면 `Queryable<TData, TFrom>` 를 받는다. immutable 체이닝으로 옵션·필터·조인·그룹을 쌓고, 종단 메서드(`execute`/`single`/`count`/`insert`/...)로 실행한다. where/select/orderBy 콜백 안에서는 [expr.md](./expr.md) 의 `expr` 로 표현식을 만든다. 프로시저는 `Executable`, 텍스트 검색 구문은 `parseSearchQuery` 가 처리한다.
|
|
4
4
|
|
|
5
|
-
`
|
|
5
|
+
표준 조회 흐름(orm.md): `db.X()` → `joinSingle` 로 도출 컬럼 부착 → `select` 로 모든 도출을 한 번에 projection → projected 컬럼 이름으로 `where`/`orderBy` → `count` → `orderBy().limit().execute()`. SELECT 절에 `expr.subquery`/`expr.exists` 를 넣지 말고 `joinSingle` 로 1회 부착, `wrap()` 은 `distinct`/`groupBy` 뒤 `count` 등 프레임워크가 요구할 때만 사용.
|
|
6
6
|
|
|
7
|
-
##
|
|
7
|
+
## Queryable — 조립 메서드 (체이닝)
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
- `distinct(): Queryable` — DISTINCT 적용. 이후 `count()` 직접 호출 불가(`wrap()` 필요).
|
|
11
|
-
- `lock(): Queryable` — FOR UPDATE 행 잠금. 트랜잭션 내 배타 잠금이 필요할 때.
|
|
9
|
+
옵션:
|
|
12
10
|
|
|
13
|
-
|
|
11
|
+
- `select(fn)` — SELECT column 재정의. `fn(columns) => R` 의 R 이 새 결과 형태. 리터럴 상수는 자동으로 ExprUnit 래핑. 호출 후 CUD 불가(`TFrom` 소실).
|
|
12
|
+
- `distinct()` — DISTINCT 적용. 이후 `count()` 하려면 `wrap()` 필요.
|
|
13
|
+
- `lock()` — 행 잠금(FOR UPDATE). 트랜잭션 안에서 선택 행 배타 잠금. CUD 가능 상태 유지.
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
- `limit(skip: number, take: number): Queryable` — 페이지네이션(OFFSET `skip`, LIMIT `take`). **ORDER BY 선행 필수**(없으면 throw).
|
|
15
|
+
제한:
|
|
17
16
|
|
|
18
|
-
|
|
17
|
+
- `top(count)` — 상위 N행. ORDER BY 없이도 사용 가능.
|
|
18
|
+
- `limit(skip, take)` — 페이지네이션 OFFSET/LIMIT. `skip`=건너뛸 수, `take`=가져올 수. **먼저 `orderBy()` 호출 필수** — 없으면 throw.
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
- `fnOrKey` — 정렬 column 을 반환하는 함수 `(cols) => ExprUnit`, 또는 체인 경로 문자열(`"id"`, `"user.name"` — `obj.getChainValue` 로 해석. 동적 정렬 루프용).
|
|
22
|
-
- `orderBy` — 방향. 기본 ASC.
|
|
20
|
+
정렬:
|
|
23
21
|
|
|
24
|
-
|
|
22
|
+
- `orderBy(fnOrKey, orderBy?)` — 정렬 추가(여러 번 호출 시 순서대로). `fnOrKey`=정렬 column 반환 함수 또는 체인 경로 문자열(`"user.name"`, 동적 정렬용). `orderBy`="ASC"|"DESC"(기본 ASC).
|
|
25
23
|
|
|
26
|
-
|
|
27
|
-
- `search(fn: (cols) => ExprUnit<string|undefined>[], searchText: string): Queryable` — 다중 column 텍스트 검색. `fn` 은 검색 대상 column 들, `searchText` 는 검색 문법 문자열(공백=OR, `+`=필수, `-`=제외, `"..."`=구문, `*`=와일드카드). 빈 문자열이면 변화 없이 self 반환. 내부적으로 `lower(col) LIKE pattern` 조합을 AND/OR 로 조립. (문법 상세: README 검색 파서)
|
|
24
|
+
필터:
|
|
28
25
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
```
|
|
26
|
+
- `where(predicate)` — WHERE 조건 추가(여러 번 호출 시 AND 결합). `predicate(columns) => WhereExprUnit[]`. 배열 내 여러 조건도 AND.
|
|
27
|
+
- `search(fn, searchText)` — 텍스트 검색. `fn(columns) => ExprUnit<string|undefined>[]`(검색 대상 column 들), `searchText`=검색 구문(아래 `parseSearchQuery` 문법). 빈 문자열이면 무변경. 공백=OR, `+`=필수(AND), `-`=제외(NOT), `"구문"`=정확 일치(필수). 내부적으로 `expr.like(expr.lower(col), pattern)` 로 대소문자 무시 매칭.
|
|
32
28
|
|
|
33
|
-
|
|
29
|
+
그룹:
|
|
34
30
|
|
|
35
|
-
- `groupBy(fn
|
|
36
|
-
- `having(predicate
|
|
31
|
+
- `groupBy(fn)` — GROUP BY. `fn(columns) => ExprUnit<ColumnPrimitive>[]`. 호출 후 CUD 불가.
|
|
32
|
+
- `having(predicate)` — GROUP BY 이후 필터. `predicate(columns) => WhereExprUnit[]`.
|
|
37
33
|
|
|
38
|
-
|
|
34
|
+
조인:
|
|
39
35
|
|
|
40
|
-
- `join
|
|
41
|
-
- `joinSingle
|
|
42
|
-
- `include(fn
|
|
36
|
+
- `join(as, fn)` — 1:N LEFT JOIN, 결과에 **배열**(`{ [as]?: R[] }`)로 부착. `fn(qr, parentCols) => Queryable` 안에서 `qr.from(Table).where(...)` 로 조인 본문 구성.
|
|
37
|
+
- `joinSingle(as, fn)` — N:1/1:1 LEFT JOIN, 결과에 **단일 객체**(`{ [as]?: R }`)로 부착. 도출 컬럼(집계·boolean)을 outer 행에 붙일 때 표준 수단.
|
|
38
|
+
- `include(fn)` — 빌더에 정의한 FK/FKT/RelationKey 관계 자동 JOIN. `fn(item) => item.user.company` 형태로 타입 안전 경로 지정(`PathProxy`, ColumnPrimitive 가 아닌 관계 필드만 접근 가능). 다단계·다중 호출 가능. 관계 미정의 시 throw.
|
|
43
39
|
|
|
44
|
-
|
|
45
|
-
db.post().include((p) => p.user.company);
|
|
46
|
-
db.user().join("posts", (qr, u) => qr.from(Post).where((p) => [expr.eq(p.userId, u.id)]));
|
|
47
|
-
```
|
|
40
|
+
서브쿼리/UNION:
|
|
48
41
|
|
|
49
|
-
|
|
42
|
+
- `wrap()` — 현재 Queryable 을 서브쿼리(derived table)로 감쌈. `distinct()`/`groupBy()` 뒤 `count()` 처럼 프레임워크가 요구할 때만.
|
|
43
|
+
- `static Queryable.union(...queries)` — 2개 이상 Queryable 을 UNION(중복 제거)으로 결합. 결과 위 fluent 연산자는 외부 union 결과에 적용. select 컬럼 이름·타입·순서를 양쪽 동일하게 맞춰야 함(orm-union.md). 2개 미만이면 throw.
|
|
50
44
|
|
|
51
|
-
|
|
52
|
-
- `static Queryable.union<TData>(...queries): Queryable<TData, never>` — 여러 Queryable UNION(중복 제거). 최소 2개, 미만 시 throw. (`JoinQueryable.union` 도 동일 의미)
|
|
53
|
-
- `recursive(fn: (qr: RecursiveQueryable) => Queryable): Queryable<TData, never>` — 재귀 CTE(WITH RECURSIVE). 계층 데이터 조회. `qr.from(table)`/`qr.select(cols)`/`qr.union(...)` 로 재귀 본문 정의, 재귀 측은 `self` 프로퍼티로 base 를 자기 참조.
|
|
45
|
+
재귀:
|
|
54
46
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
47
|
+
- `recursive(fn)` — WITH RECURSIVE CTE 생성(계층 데이터). `fn(cte) => cte.from(Table).where(...)` 안에서 `e.self[0]` 로 직전 단계 행을 참조.
|
|
48
|
+
|
|
49
|
+
`db.user().join(...)`/`recursive(...)`/`Queryable.union(...)` 안에서 쓰는 `JoinQueryable`·`RecursiveQueryable` 의 `from`/`select`/`union` 은 콜백 인자로만 노출되며 직접 import 하지 않는다.
|
|
58
50
|
|
|
59
|
-
##
|
|
51
|
+
## Queryable — 종단 메서드
|
|
52
|
+
|
|
53
|
+
조회:
|
|
60
54
|
|
|
61
55
|
- `execute(): Promise<TData[]>` — SELECT 실행, 결과 배열.
|
|
62
56
|
- `single(): Promise<TData | undefined>` — 단일 결과. 2건 이상이면 throw.
|
|
63
|
-
- `first(): Promise<TData | undefined>` — 첫
|
|
64
|
-
- `count(fn
|
|
65
|
-
- `exists(): Promise<boolean>` — 조건
|
|
66
|
-
|
|
57
|
+
- `first(): Promise<TData | undefined>` — 첫 행만(내부적으로 `top(1)`). 복수여도 에러 없음.
|
|
58
|
+
- `count(fn?): Promise<number>` — 행 수. `fn?`=셀 column 지정(미지정 시 전체). `distinct()`/`groupBy()` 직후 호출하면 throw(→ `wrap()` 먼저).
|
|
59
|
+
- `exists(): Promise<boolean>` — 조건 매칭 행 존재 여부(내부 `top(1)`).
|
|
60
|
+
|
|
61
|
+
CUD(모두 `TFrom` 이 TableBuilder 일 때만 — `select`/`groupBy` 후엔 불가):
|
|
62
|
+
|
|
63
|
+
- `insert(records)` / `insert(records, outputColumns)` — 다건 INSERT. `records: $inferInsert[]`. MSSQL 1000행 제한 때문에 1000개씩 청크 분할. `outputColumns: K[]` 지정 시 삽입된 행에서 그 column 만 배열로 반환(AI/PK 값 회수). 빈 배열이면 no-op.
|
|
64
|
+
- `insertIfNotExists(record)` / `(record, outputColumns)` — 현재 WHERE 조건 매칭 행이 없을 때만 1건 INSERT. output 지정 시 삽입 행 1개 반환.
|
|
65
|
+
- `insertInto(targetTable)` / `(targetTable, outputColumns)` — 현재 SELECT 결과를 다른 테이블에 INSERT INTO ... SELECT. `targetTable` 의 column 구조가 현재 결과와 호환해야 함.
|
|
66
|
+
- `update(recordFwd)` / `(recordFwd, outputColumns)` — UPDATE. `recordFwd(cols) => QueryableWriteRecord<$inferUpdate>`(갱신할 column→값). 값은 `ExprInput`(리터럴 직접 가능, `expr.val` 불필요). output 지정 시 갱신 행 반환.
|
|
67
|
+
- `delete()` / `delete(outputColumns)` — DELETE. output 지정 시 삭제 행 반환.
|
|
68
|
+
- `upsert(updateFn)` / `upsert(insertFn, outputColumns?)` / `upsert(updateFn, insertFn)` / `upsert(updateFn, insertFn, outputColumns)` — WHERE 매칭 행 있으면 UPDATE, 없으면 INSERT(MERGE). `updateFn(cols) => 갱신값`, `insertFn(updateRecord) => 삽입값`(생략 시 update 값 재사용). output 지정 시 영향 행 반환.
|
|
69
|
+
|
|
70
|
+
QueryDef 생성기(실행 없이 AST 만): `getSelectQueryDef()`, `getInsertQueryDef(records, outputColumns?)`, `getInsertIfNotExistsQueryDef(...)`, `getInsertIntoQueryDef(...)`, `getUpdateQueryDef(...)`, `getDeleteQueryDef(...)`, `getUpsertQueryDef(...)`, `getResultMeta(outputColumns?)`. executor 우회·디버깅·배치 조립용.
|
|
67
71
|
|
|
68
|
-
|
|
72
|
+
기타:
|
|
69
73
|
|
|
70
|
-
|
|
71
|
-
- `
|
|
72
|
-
- `insertIfNotExists(record, outputColumns?): Promise<void | Pick<...>>` — 현재 WHERE 조건 매칭이 없을 때만 INSERT.
|
|
73
|
-
- `insertInto(targetTable, outputColumns?): Promise<void | Pick<...>[]>` — 현재 SELECT 결과를 다른 테이블에 INSERT INTO ... SELECT. `targetTable` column 구조가 현재 데이터와 호환되어야 함(타입 매칭).
|
|
74
|
-
- `getInsertQueryDef` / `getInsertIfNotExistsQueryDef` / `getInsertIntoQueryDef` — 각 def 생성기.
|
|
74
|
+
- `switchFk(enabled)` — 이 Queryable 의 소스 테이블 FK 제약을 활성/비활성. 트랜잭션 안에서 가능.
|
|
75
|
+
- `readonly meta` — 내부 조립 상태(from/where/joins/columns 등). 직접 수정하지 않음.
|
|
75
76
|
|
|
76
77
|
```typescript
|
|
77
|
-
|
|
78
|
+
// 표준 조회 (orm.md 흐름)
|
|
79
|
+
const items = await db.order()
|
|
80
|
+
.joinSingle("state", (q, p) =>
|
|
81
|
+
q.from(OrderLine).where((l) => [expr.eq(l.orderId, p.id)])
|
|
82
|
+
.select((l) => ({ sumQty: expr.sum(l.qty), doneCnt: expr.count(l.doneAt) })),
|
|
83
|
+
)
|
|
84
|
+
.select((p) => ({
|
|
85
|
+
id: p.id,
|
|
86
|
+
sumQty: expr.coalesce(p.state!.sumQty, 0),
|
|
87
|
+
isDone: expr.gt(p.state!.doneCnt, 0),
|
|
88
|
+
}))
|
|
89
|
+
.where((r) => [expr.eq(r.isDone, true)])
|
|
90
|
+
.orderBy((r) => r.id, "DESC")
|
|
91
|
+
.limit(0, 50)
|
|
92
|
+
.execute();
|
|
93
|
+
|
|
94
|
+
// CUD — 리터럴 직접 전달
|
|
95
|
+
await db.user().where((u) => [expr.eq(u.id, 1)]).update((u) => ({ name: "새이름" }));
|
|
96
|
+
const [inserted] = await db.user().insert([{ name: "홍길동" }], ["id"]);
|
|
78
97
|
```
|
|
79
98
|
|
|
80
|
-
##
|
|
99
|
+
## queryable / executable (팩토리 함수)
|
|
81
100
|
|
|
82
|
-
|
|
83
|
-
- `delete(outputColumns?): Promise<void | Pick<...>[]>` — DELETE. WHERE 미리 체이닝.
|
|
84
|
-
- `getUpdateQueryDef` / `getDeleteQueryDef` — def 생성기.
|
|
101
|
+
`DbContext` 내부에서 `this.queryable()`/`this.executable()` 로 쓰는 게 일반적이지만, 모듈 함수 형태도 export 됨.
|
|
85
102
|
|
|
86
|
-
|
|
103
|
+
- `queryable(db, tableOrView, as?)` — `() => Queryable` 팩토리. `as?` 미지정 시 호출마다 새 alias(`db.getNextAlias()`), 지정 시 고정. 반환 Queryable 의 column 은 빌더 정의로부터 구성(View 는 `viewFn` 평가).
|
|
104
|
+
- `executable(db, builder)` — `() => Executable` 팩토리.
|
|
87
105
|
|
|
88
|
-
|
|
89
|
-
- `updateFn: (cols) => QueryableWriteRecord` — 갱신/삽입 공통값(insertFn 생략 시 INSERT 도 이 값 사용).
|
|
90
|
-
- `insertFn?: (updateRecord) => QueryableWriteRecord` — INSERT 전용값(update 결과를 받아 변형). UPDATE/INSERT 데이터가 다를 때.
|
|
91
|
-
- `outputColumns?` — 반환 column.
|
|
92
|
-
- `getUpsertQueryDef(...)` — def 생성기.
|
|
106
|
+
## Executable (프로시저 실행)
|
|
93
107
|
|
|
94
108
|
```typescript
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
109
|
+
class Executable<TParams, TReturns> {
|
|
110
|
+
getExecProcQueryDef(params?): ExecProcQueryDef;
|
|
111
|
+
execute(params): Promise<InferColumnExprs<TReturns>[][]>;
|
|
112
|
+
}
|
|
98
113
|
```
|
|
99
114
|
|
|
100
|
-
|
|
115
|
+
- `execute(params)` — 프로시저 실행. `params`=`returns`/`params` 정의에 맞는 `InferColumnExprs<TParams>`(리터럴 또는 ExprUnit). 결과는 **결과셋 배열의 배열**(다중 SELECT 가능) — 단일 결과셋이면 `result[0]`.
|
|
116
|
+
- `getExecProcQueryDef(params?)` — 실행 없이 QueryDef 반환. params 가 있는데 프로시저에 파라미터 정의가 없으면 throw.
|
|
117
|
+
|
|
118
|
+
```typescript
|
|
119
|
+
const [rows] = await db.getUserById().execute({ userId: 1n });
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## parseSearchQuery / ParsedSearchQuery
|
|
123
|
+
|
|
124
|
+
`search()` 가 내부로 쓰지만 직접 호출도 가능. 검색 구문 문자열을 SQL LIKE 패턴으로 분해.
|
|
125
|
+
|
|
126
|
+
```typescript
|
|
127
|
+
function parseSearchQuery(searchText: string): ParsedSearchQuery;
|
|
128
|
+
interface ParsedSearchQuery { or: string[]; must: string[]; not: string[]; }
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
- `or: string[]` — 일반 검색어(공백 구분, OR). 와일드카드 없으면 `%term%`(부분 일치).
|
|
132
|
+
- `must: string[]` — `+term` 또는 `"정확 구문"`(AND 필수).
|
|
133
|
+
- `not: string[]` — `-term`(NOT 제외).
|
|
134
|
+
- 와일드카드 `*` → `%`(`app*`→`app%` 시작 일치). 이스케이프: `\\` `\*` `\%` `\"` `\+` `\-` 는 리터럴. 닫히지 않은 `"` 는 일반 텍스트로 처리.
|
|
135
|
+
|
|
136
|
+
```typescript
|
|
137
|
+
parseSearchQuery('apple "delicious fruit" -banana +strawberry');
|
|
138
|
+
// { or: ["%apple%"], must: ["%delicious fruit%", "%strawberry%"], not: ["%banana%"] }
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
## getMatchedPrimaryKeys
|
|
142
|
+
|
|
143
|
+
```typescript
|
|
144
|
+
function getMatchedPrimaryKeys(fkCols: string[], targetTable: TableBuilder): string[];
|
|
145
|
+
```
|
|
101
146
|
|
|
102
|
-
- `
|
|
103
|
-
- `queryable(db, tableOrView, as?): () => Queryable` — Table/View 용 Queryable 팩토리 함수(보통 `DbContext.queryable()` 가 호출). `as` 미지정 시 자동 alias.
|
|
104
|
-
- `getMatchedPrimaryKeys(fkCols, targetTable): string[]` — FK column 배열과 대상 PK 매칭(개수 불일치 시 throw). include 내부 헬퍼.
|
|
147
|
+
- FK column 배열을 대상 테이블 PK 와 매칭해 PK column 이름 배열 반환. 개수 불일치 시 throw. `include()` 가 관계 조건을 만들 때 내부 사용 — 직접 호출은 드묾.
|
|
105
148
|
|
|
106
|
-
##
|
|
149
|
+
## 결과/입력 변환 타입
|
|
107
150
|
|
|
108
|
-
- `QueryableRecord<TData>` —
|
|
109
|
-
- `QueryableWriteRecord<TData>` —
|
|
110
|
-
- `UnwrapQueryableRecord<R>` — `select`
|
|
111
|
-
- `PathProxy<TObject>` — `include` 경로
|
|
151
|
+
- `QueryableRecord<TData>` — 결과 데이터 → column 프록시 타입. 각 ColumnPrimitive 가 `ExprUnit<T>`, 중첩 관계는 재귀. where/select 등 콜백 인자 타입.
|
|
152
|
+
- `QueryableWriteRecord<TData>` — 쓰기용. 각 필드가 `ExprInput<T>`. `update`/`upsert` 콜백 반환 타입.
|
|
153
|
+
- `UnwrapQueryableRecord<R>` — `select` 결과를 다시 데이터 타입으로 역추론(ExprUnit→값).
|
|
154
|
+
- `PathProxy<TObject>` — `include` 경로 지정용 프록시 타입(관계 필드만 접근).
|
|
@@ -1,21 +1,39 @@
|
|
|
1
|
-
# @simplysm/orm-common — 스키마 정의
|
|
1
|
+
# @simplysm/orm-common — 스키마 정의 (Table / View / Procedure / column / index / relation)
|
|
2
2
|
|
|
3
|
-
DB 객체(Table/View/Procedure)와 그 구성요소(
|
|
3
|
+
DB 객체(Table/View/Procedure)와 그 구성요소(column/index/관계)를 fluent 빌더로 선언하는 묶음. 모든 빌더는 immutable — 각 메서드가 새 인스턴스를 반환한다. 정의한 빌더는 `DbContext` 안에서 `this.queryable()`/`this.executable()` 로 등록한다. column 은 기본 `NOT NULL`; `.nullable()`/`.default(...)` 는 도메인 근거가 있을 때만 붙인다(orm.md).
|
|
4
4
|
|
|
5
5
|
## Table / TableBuilder
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
```typescript
|
|
8
|
+
function Table<TName extends string>(name: TName): TableBuilder<TName, {}, {}>;
|
|
9
|
+
|
|
10
|
+
class TableBuilder<TName, TColumns, TRelations> {
|
|
11
|
+
readonly meta: { name; description?; database?; schema?; columns?; primaryKey?; relations?; indexes? };
|
|
12
|
+
readonly $inferSelect; // columns + 관계(optional)
|
|
13
|
+
readonly $inferColumns; // column 값 타입만
|
|
14
|
+
readonly $inferInsert; // INSERT 타입 (autoIncrement/nullable/default 는 optional)
|
|
15
|
+
readonly $inferUpdate; // UPDATE 타입 (전부 optional)
|
|
16
|
+
|
|
17
|
+
description(desc: string): TableBuilder;
|
|
18
|
+
database(db: string): TableBuilder;
|
|
19
|
+
schema(schema: string): TableBuilder;
|
|
20
|
+
columns(fn: (c) => TNewColumns): TableBuilder;
|
|
21
|
+
primaryKey(...columns: (keyof TColumns)[]): TableBuilder;
|
|
22
|
+
indexes(fn: (i) => IndexBuilder[]): TableBuilder;
|
|
23
|
+
relations(fn: (r) => TRelations): TableBuilder;
|
|
24
|
+
}
|
|
25
|
+
```
|
|
8
26
|
|
|
9
|
-
- `Table
|
|
10
|
-
- `
|
|
11
|
-
- `
|
|
12
|
-
- `
|
|
13
|
-
- `
|
|
14
|
-
- `
|
|
15
|
-
- `
|
|
16
|
-
- `
|
|
17
|
-
- `
|
|
18
|
-
-
|
|
27
|
+
- `Table(name)` — 빈 `TableBuilder` 생성. 이후 fluent 메서드로 채운다.
|
|
28
|
+
- `description(desc)` — 테이블 설명. CREATE TABLE 시 DDL comment 로 들어감.
|
|
29
|
+
- `database(db)` — 데이터베이스명 고정. 미지정 시 `DbContext` 의 기본 database.
|
|
30
|
+
- `schema(schema)` — 스키마명(MSSQL/PostgreSQL). MySQL 무시.
|
|
31
|
+
- `columns(fn)` — column factory `c` 를 받아 `{ name: c.타입() }` 레코드를 반환. 아래 column factory 참조. 호출 후 `$inferColumns` 등 타입이 갱신.
|
|
32
|
+
- `primaryKey(...columns)` — PK column 이름(들). 여러 개 넘기면 복합 PK.
|
|
33
|
+
- `indexes(fn)` — index factory `i` 를 받아 `IndexBuilder[]` 반환.
|
|
34
|
+
- `relations(fn)` — relation factory `r` 를 받아 FK/역참조 관계 레코드 반환. Table 은 `foreignKey`/`foreignKeyTarget`/`relationKey`/`relationKeyTarget` 모두 사용 가능.
|
|
35
|
+
- `$inferSelect` — SELECT 결과 타입(컬럼 + include 가능한 관계는 optional). `queryable` 콜백 인자의 원천.
|
|
36
|
+
- `$inferInsert` / `$inferUpdate` — `insert`/`update` 입력 타입. INSERT 는 autoIncrement·nullable·default column 이 optional, UPDATE 는 전부 optional.
|
|
19
37
|
|
|
20
38
|
```typescript
|
|
21
39
|
const User = Table("User")
|
|
@@ -26,42 +44,62 @@ const User = Table("User")
|
|
|
26
44
|
email: c.varchar(200).nullable(),
|
|
27
45
|
}))
|
|
28
46
|
.primaryKey("id")
|
|
29
|
-
.indexes((i) => [i.index("email").unique()])
|
|
30
|
-
.relations((r) => ({ posts: r.foreignKeyTarget(() => Post, "author") }));
|
|
47
|
+
.indexes((i) => [i.index("email").unique()]);
|
|
31
48
|
```
|
|
32
49
|
|
|
33
50
|
## View / ViewBuilder
|
|
34
51
|
|
|
35
|
-
|
|
52
|
+
```typescript
|
|
53
|
+
function View(name: string): ViewBuilder;
|
|
54
|
+
|
|
55
|
+
class ViewBuilder<TDbContext, TData, TRelations> {
|
|
56
|
+
readonly meta: { name; description?; database?; schema?; viewFn?; relations? };
|
|
57
|
+
readonly $inferSelect; // TData
|
|
58
|
+
|
|
59
|
+
description(desc): ViewBuilder;
|
|
60
|
+
database(db): ViewBuilder;
|
|
61
|
+
schema(schema): ViewBuilder;
|
|
62
|
+
query(viewFn: (db) => Queryable<TViewData, any>): ViewBuilder;
|
|
63
|
+
relations(fn: (r) => T): ViewBuilder;
|
|
64
|
+
}
|
|
65
|
+
```
|
|
36
66
|
|
|
37
|
-
- `View(name
|
|
38
|
-
- `
|
|
39
|
-
- `
|
|
40
|
-
- `
|
|
41
|
-
- `ViewBuilder.query(viewFn: (db) => Queryable)` — 뷰 본문 SELECT 정의. `db` 는 DbContext, 반환은 Queryable.
|
|
42
|
-
- `ViewBuilder.relations(fn: (r) => ...)` — 관계 정의(`relationKey`/`relationKeyTarget` 만).
|
|
43
|
-
- `ViewBuilder.meta` — `name`/`description`/`database`/`schema`/`viewFn`/`relations`.
|
|
44
|
-
- `$inferSelect` — 뷰 데이터 타입(타입 추론용).
|
|
67
|
+
- `View(name)` — 빈 `ViewBuilder` 생성.
|
|
68
|
+
- `query(viewFn)` — 뷰 본문 SELECT 를 `db` 를 받는 함수로 정의. 반환 `Queryable` 의 select 결과가 뷰 데이터 타입이 됨. `queryable(this, View)` 등록 시 이 함수가 평가되어 column 이 구성된다.
|
|
69
|
+
- `relations(fn)` — 뷰는 `relationKey`/`relationKeyTarget` 만 사용 가능(DB FK 미생성). `foreignKey` 는 타입상 불가.
|
|
70
|
+
- `description`/`database`/`schema` — Table 과 동일 의미.
|
|
45
71
|
|
|
46
72
|
```typescript
|
|
47
73
|
const ActiveUsers = View("ActiveUsers")
|
|
48
74
|
.database("mydb")
|
|
49
|
-
.query((db:
|
|
75
|
+
.query((db: MainDb) =>
|
|
76
|
+
db.user().where((u) => [expr.eq(u.status, "active")]).select((u) => ({ id: u.id, name: u.name })),
|
|
77
|
+
);
|
|
50
78
|
```
|
|
51
79
|
|
|
52
80
|
## Procedure / ProcedureBuilder
|
|
53
81
|
|
|
54
|
-
|
|
82
|
+
```typescript
|
|
83
|
+
function Procedure(name: string): ProcedureBuilder<never, never>;
|
|
84
|
+
|
|
85
|
+
class ProcedureBuilder<TParams, TReturns> {
|
|
86
|
+
readonly meta: { name; description?; database?; schema?; params?; returns?; query? };
|
|
87
|
+
readonly $params; readonly $returns;
|
|
88
|
+
|
|
89
|
+
description(desc): ProcedureBuilder;
|
|
90
|
+
database(db): ProcedureBuilder;
|
|
91
|
+
schema(schema): ProcedureBuilder;
|
|
92
|
+
params(fn: (c) => T): ProcedureBuilder;
|
|
93
|
+
returns(fn: (c) => T): ProcedureBuilder;
|
|
94
|
+
body(sql: string): ProcedureBuilder;
|
|
95
|
+
}
|
|
96
|
+
```
|
|
55
97
|
|
|
56
|
-
- `Procedure(name
|
|
57
|
-
- `
|
|
58
|
-
- `
|
|
59
|
-
- `
|
|
60
|
-
- `
|
|
61
|
-
- `ProcedureBuilder.returns(fn: (c) => Record<string, ColumnBuilder>)` — 반환 결과 column 정의.
|
|
62
|
-
- `ProcedureBuilder.body(sql: string)` — 본문 SQL. DBMS별 구문 차이 주의(MySQL: `param`, MSSQL: `@param`, PostgreSQL: `RETURN QUERY` 필요).
|
|
63
|
-
- `ProcedureBuilder.meta` — `name`/`description`/`database`/`schema`/`params`/`returns`/`query`.
|
|
64
|
-
- `$params` / `$returns` — 타입 추론용.
|
|
98
|
+
- `Procedure(name)` — 빈 빌더 생성.
|
|
99
|
+
- `params(fn)` — 입력 파라미터를 column factory 로 정의. `Executable.execute(params)` 의 입력 타입이 됨.
|
|
100
|
+
- `returns(fn)` — 결과 column 정의. `execute` 결과 행 타입이 됨.
|
|
101
|
+
- `body(sql)` — 프로시저 본문 SQL. DBMS별 구문 차이 주의(MySQL: `userId`, MSSQL: `@userId`, PostgreSQL: `RETURN QUERY` 필요).
|
|
102
|
+
- `description`/`database`/`schema` — Table 과 동일.
|
|
65
103
|
|
|
66
104
|
```typescript
|
|
67
105
|
const GetUserById = Procedure("GetUserById")
|
|
@@ -71,51 +109,52 @@ const GetUserById = Procedure("GetUserById")
|
|
|
71
109
|
.body("SELECT id, name FROM User WHERE id = userId");
|
|
72
110
|
```
|
|
73
111
|
|
|
74
|
-
##
|
|
75
|
-
|
|
76
|
-
`columns
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
- `
|
|
81
|
-
- `
|
|
82
|
-
- `
|
|
83
|
-
- `
|
|
84
|
-
- `
|
|
85
|
-
- `
|
|
86
|
-
- `
|
|
87
|
-
- `
|
|
88
|
-
- `
|
|
89
|
-
- `
|
|
90
|
-
- `
|
|
91
|
-
- `
|
|
92
|
-
- `
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
-
|
|
98
|
-
-
|
|
99
|
-
- `
|
|
112
|
+
## column factory / ColumnBuilder
|
|
113
|
+
|
|
114
|
+
`columns`/`params`/`returns` 콜백 인자 `c` 가 column factory. 각 메서드는 `ColumnBuilder` 를 반환하고 `.autoIncrement()`/`.nullable()`/`.default()`/`.description()` 로 속성을 더한다(모두 immutable).
|
|
115
|
+
|
|
116
|
+
타입 메서드:
|
|
117
|
+
|
|
118
|
+
- `int()` — INT(4바이트 정수).
|
|
119
|
+
- `bigint()` — BIGINT(8바이트 정수). autoIncrement PK 에 주로 사용.
|
|
120
|
+
- `float()` — FLOAT(단정밀도 실수).
|
|
121
|
+
- `double()` — DOUBLE(배정밀도 실수).
|
|
122
|
+
- `decimal(precision, scale?)` — 고정 소수점. `precision`=전체 자릿수, `scale`=소수 자릿수(선택). 금액 등 정밀도 필요 시.
|
|
123
|
+
- `varchar(length)` — 가변 길이 문자열. `length`=최대 길이.
|
|
124
|
+
- `char(length)` — 고정 길이 문자열.
|
|
125
|
+
- `text()` — 대용량 텍스트.
|
|
126
|
+
- `binary()` — 바이너리(MySQL LONGBLOB / MSSQL VARBINARY(MAX) / PostgreSQL BYTEA). 값 타입 `Bytes`.
|
|
127
|
+
- `boolean()` — 불리언(MySQL TINYINT(1) / MSSQL BIT / PostgreSQL BOOLEAN).
|
|
128
|
+
- `datetime()` — 날짜+시간. 값 타입 `DateTime`.
|
|
129
|
+
- `date()` — 날짜만. 값 타입 `DateOnly`.
|
|
130
|
+
- `time()` — 시간만. 값 타입 `Time`.
|
|
131
|
+
- `uuid()` — UUID(MySQL BINARY(16) / MSSQL UNIQUEIDENTIFIER / PostgreSQL UUID). 값 타입 `Uuid`.
|
|
132
|
+
|
|
133
|
+
`ColumnBuilder` 속성 메서드:
|
|
134
|
+
|
|
135
|
+
- `autoIncrement()` — INSERT 시 자동 증가. INSERT 타입에서 optional 처리. PK 자동 증가 column 에.
|
|
136
|
+
- `nullable()` — NULL 허용. 값 타입에 `undefined` 추가, INSERT optional. 도메인상 값이 없을 수 있을 때만.
|
|
137
|
+
- `default(value)` — INSERT 시 미지정이면 사용할 기본값. INSERT optional 처리. 사용자가 명시 지시한 경우에만.
|
|
138
|
+
- `description(desc)` — column 설명(DDL comment).
|
|
100
139
|
|
|
101
140
|
```typescript
|
|
102
141
|
.columns((c) => ({
|
|
103
142
|
id: c.bigint().autoIncrement(),
|
|
104
|
-
|
|
143
|
+
price: c.decimal(10, 2),
|
|
105
144
|
email: c.varchar(200).nullable(),
|
|
145
|
+
status: c.varchar(20).default("active"),
|
|
106
146
|
}))
|
|
107
147
|
```
|
|
108
148
|
|
|
109
|
-
##
|
|
149
|
+
## index factory / IndexBuilder
|
|
110
150
|
|
|
111
|
-
`indexes
|
|
151
|
+
`indexes` 콜백 인자 `i` 의 `index(...columns)` 로 시작해 fluent 로 옵션을 더한다.
|
|
112
152
|
|
|
113
|
-
- `i.index(...columns
|
|
114
|
-
-
|
|
115
|
-
-
|
|
116
|
-
-
|
|
117
|
-
-
|
|
118
|
-
- `IndexBuilder.meta` — `columns`/`name?`/`unique?`/`orderBy?`/`description?`.
|
|
153
|
+
- `i.index(...columns)` — index 대상 column 이름(들). 여러 개면 복합 index.
|
|
154
|
+
- `.name(name)` — index 이름 지정. 미지정 시 자동 생성.
|
|
155
|
+
- `.unique()` — 유니크 index.
|
|
156
|
+
- `.orderBy(...orderBy)` — column별 정렬 방향 배열("ASC"|"DESC"). column 수와 길이 일치해야 함.
|
|
157
|
+
- `.description(description)` — index 설명(DDL comment).
|
|
119
158
|
|
|
120
159
|
```typescript
|
|
121
160
|
.indexes((i) => [
|
|
@@ -124,39 +163,42 @@ modifier(생성된 ColumnBuilder 에 체이닝):
|
|
|
124
163
|
])
|
|
125
164
|
```
|
|
126
165
|
|
|
127
|
-
##
|
|
166
|
+
## relation factory
|
|
128
167
|
|
|
129
|
-
Table
|
|
168
|
+
`relations` 콜백 인자 `r`. Table 은 4종 모두, View 는 `relationKey`/`relationKeyTarget` 만. 대상 빌더는 순환 참조 방지를 위해 모두 `() => Target` 지연 함수로 넘긴다. `description`/`single` 은 메서드 체이닝이 아니라 마지막 `opts` 인자로 전달(체이닝은 TS7022 유발로 제거됨).
|
|
130
169
|
|
|
131
|
-
- `r.foreignKey(columns
|
|
132
|
-
- `r.foreignKeyTarget(
|
|
133
|
-
- `r.relationKey(columns,
|
|
134
|
-
- `r.relationKeyTarget(
|
|
170
|
+
- `r.foreignKey(columns, () => Target, opts?)` — N:1 FK 관계(DB FK 제약 **생성**). `columns`=현재 테이블 FK column 배열, 대상은 그 테이블의 PK 와 매칭. `opts.description?`. → `ForeignKeyBuilder`.
|
|
171
|
+
- `r.foreignKeyTarget(() => Target, relationName, opts?)` — FK 역참조(1:N, DB FK 생성 측의 역방향). `relationName`=대상 테이블에서 이쪽을 가리키는 FK 관계 이름. `opts.single: true` 면 1:1 단일 객체, 아니면 배열. `opts.description?`. → `ForeignKeyTargetBuilder`.
|
|
172
|
+
- `r.relationKey(columns, () => Target, opts?)` — N:1 논리 관계(DB FK **미생성**). FK 와 동일하나 제약 없음. View 에서도 사용. → `RelationKeyBuilder`.
|
|
173
|
+
- `r.relationKeyTarget(() => Target, relationName, opts?)` — 1:N/1:1 논리 역참조(DB FK 미생성). `opts.single`/`opts.description` 동일. → `RelationKeyTargetBuilder`.
|
|
135
174
|
|
|
136
|
-
각
|
|
137
|
-
- `ForeignKeyBuilder.meta` / `RelationKeyBuilder.meta` — `ownerFn`/`columns`/`targetFn`/`description?`.
|
|
138
|
-
- `ForeignKeyTargetBuilder.meta` / `RelationKeyTargetBuilder.meta` — `targetTableFn`/`relationName`/`description?`/`isSingle?`.
|
|
175
|
+
각 `opts`:
|
|
139
176
|
|
|
140
|
-
|
|
177
|
+
- `description?: string` — 관계 설명.
|
|
178
|
+
- `single?: boolean` (target 계열만) — true=결과를 단일 객체(1:1), false/미지정=배열(1:N). `include()`/`$inferSelect` 의 해당 키 타입이 단일/배열로 갈린다.
|
|
141
179
|
|
|
142
180
|
```typescript
|
|
143
181
|
const Post = Table("Post")
|
|
144
182
|
.columns((c) => ({ id: c.bigint().autoIncrement(), authorId: c.bigint() }))
|
|
145
183
|
.primaryKey("id")
|
|
146
184
|
.relations((r) => ({ author: r.foreignKey(["authorId"], () => User, { description: "작성자" }) }));
|
|
185
|
+
|
|
186
|
+
const User = Table("User")
|
|
187
|
+
.columns((c) => ({ id: c.bigint().autoIncrement(), name: c.varchar(100) }))
|
|
188
|
+
.primaryKey("id")
|
|
189
|
+
.relations((r) => ({
|
|
190
|
+
posts: r.foreignKeyTarget(() => Post, "author"),
|
|
191
|
+
profile: r.foreignKeyTarget(() => Profile, "user", { single: true }),
|
|
192
|
+
}));
|
|
147
193
|
```
|
|
148
194
|
|
|
149
|
-
|
|
195
|
+
주의: FK column 개수와 대상 테이블 PK 개수가 다르면 `include()`/join 시 "FK/PK column count mismatch" throw. PK 가 복합이면 FK column 도 같은 순서·개수로.
|
|
150
196
|
|
|
151
|
-
|
|
197
|
+
## 빌더/추론 타입 (직접 참조 드묾)
|
|
152
198
|
|
|
153
|
-
- `ColumnBuilderRecord` — `Record<string, ColumnBuilder
|
|
154
|
-
- `
|
|
155
|
-
- `InferColumnExprs<
|
|
156
|
-
- `
|
|
157
|
-
- `
|
|
158
|
-
- `
|
|
159
|
-
- `DataToColumnBuilderRecord<TData>` — 데이터 레코드 → column 빌더 레코드(insertInto 타입 매칭용).
|
|
160
|
-
- `RelationBuilderRecord` — 4개 관계 빌더 union 의 레코드.
|
|
161
|
-
- `InferDeepRelations<TRelations, TVisited?>` — 관계 정의 → 심층 관계 타입(전부 optional, 동일 테이블 재방문 시 순환 차단).
|
|
162
|
-
- `ExtractRelationTarget` / `ExtractRelationTargetResult` — 단일(N:1)/배열·단일(1:N) 대상 타입 추출.
|
|
199
|
+
- `ColumnBuilderRecord` — `Record<string, ColumnBuilder<...>>`. `columns`/`params`/`returns` 반환 타입.
|
|
200
|
+
- `RelationBuilderRecord` — 4종 relation 빌더의 union 레코드. `relations` 반환 타입.
|
|
201
|
+
- `InferColumns<T>` / `InferInsertColumns<T>` / `InferUpdateColumns<T>` / `InferColumnExprs<T>` — column 레코드에서 각각 값 타입 / INSERT 입력 / UPDATE 입력 / `ExprInput` 입력 타입을 추론. `$inferColumns` 등 내부 사용.
|
|
202
|
+
- `RequiredInsertKeys<T>` / `OptionalInsertKeys<T>` — INSERT 시 필수/선택 column key 분리(autoIncrement·nullable·default 가 선택).
|
|
203
|
+
- `DataToColumnBuilderRecord<TData>` — 데이터 레코드 → column 빌더 레코드 역변환. `insertInto` 의 대상 테이블 제약에 사용.
|
|
204
|
+
- `InferDeepRelations<T>` / `ExtractRelationTarget<T>` / `ExtractRelationTargetResult<T>` — 관계 정의에서 심층 결과 타입을 optional 로 추론(같은 테이블 재방문 시 순환 차단). `$inferSelect` 의 관계 부분.
|