@travetto/model-sql 6.0.0-rc.1 → 6.0.0-rc.3
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/__index__.ts +7 -5
- package/package.json +7 -7
- package/src/connection/decorator.ts +1 -1
- package/src/dialect/base.ts +46 -45
- package/src/internal/types.ts +3 -2
- package/src/service.ts +26 -33
- package/src/table-manager.ts +5 -5
- package/src/types.ts +11 -0
- package/src/{internal/util.ts → util.ts} +10 -20
- package/support/test/query.ts +3 -3
package/__index__.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
export * from './src/config';
|
|
2
|
-
export * from './src/service';
|
|
3
|
-
export * from './src/connection/base';
|
|
4
|
-
export * from './src/connection/decorator';
|
|
5
|
-
export * from './src/dialect/base';
|
|
1
|
+
export * from './src/config.ts';
|
|
2
|
+
export * from './src/service.ts';
|
|
3
|
+
export * from './src/connection/base.ts';
|
|
4
|
+
export * from './src/connection/decorator.ts';
|
|
5
|
+
export * from './src/dialect/base.ts';
|
|
6
|
+
export * from './src/util.ts';
|
|
7
|
+
export * from './src/types.ts';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@travetto/model-sql",
|
|
3
|
-
"version": "6.0.0-rc.
|
|
3
|
+
"version": "6.0.0-rc.3",
|
|
4
4
|
"description": "SQL backing for the travetto model module, with real-time modeling support for SQL schemas.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"sql",
|
|
@@ -27,14 +27,14 @@
|
|
|
27
27
|
"directory": "module/model-sql"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@travetto/cli": "^6.0.0-rc.
|
|
31
|
-
"@travetto/config": "^6.0.0-rc.
|
|
32
|
-
"@travetto/context": "^6.0.0-rc.
|
|
33
|
-
"@travetto/model": "^6.0.0-rc.
|
|
34
|
-
"@travetto/model-query": "^6.0.0-rc.
|
|
30
|
+
"@travetto/cli": "^6.0.0-rc.3",
|
|
31
|
+
"@travetto/config": "^6.0.0-rc.2",
|
|
32
|
+
"@travetto/context": "^6.0.0-rc.2",
|
|
33
|
+
"@travetto/model": "^6.0.0-rc.2",
|
|
34
|
+
"@travetto/model-query": "^6.0.0-rc.2"
|
|
35
35
|
},
|
|
36
36
|
"peerDependencies": {
|
|
37
|
-
"@travetto/test": "^6.0.0-rc.
|
|
37
|
+
"@travetto/test": "^6.0.0-rc.2"
|
|
38
38
|
},
|
|
39
39
|
"peerDependenciesMeta": {
|
|
40
40
|
"@travetto/test": {
|
package/src/dialect/base.ts
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
/* eslint-disable @stylistic/indent */
|
|
2
|
-
import { DataUtil, SchemaRegistry, FieldConfig, Schema } from '@travetto/schema';
|
|
3
|
-
import { Class, AppError, TypedObject, TimeUtil, castTo, castKey } from '@travetto/runtime';
|
|
4
|
-
import { SelectClause, Query, SortClause, WhereClause, RetainFields } from '@travetto/model-query';
|
|
5
|
-
import { BulkResponse, IndexConfig } from '@travetto/model';
|
|
6
|
-
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
|
|
10
|
-
import {
|
|
11
|
-
|
|
12
|
-
|
|
2
|
+
import { DataUtil, SchemaRegistry, FieldConfig, Schema, type Point } from '@travetto/schema';
|
|
3
|
+
import { Class, AppError, TypedObject, TimeUtil, castTo, castKey, toConcrete } from '@travetto/runtime';
|
|
4
|
+
import { SelectClause, Query, SortClause, WhereClause, RetainFields, ModelQueryUtil } from '@travetto/model-query';
|
|
5
|
+
import { BulkResponse, IndexConfig, ModelType } from '@travetto/model';
|
|
6
|
+
|
|
7
|
+
import { SQLModelUtil } from '../util.ts';
|
|
8
|
+
import { DeleteWrapper, InsertWrapper, DialectState } from '../internal/types.ts';
|
|
9
|
+
import { Connection } from '../connection/base.ts';
|
|
10
|
+
import { VisitStack } from '../types.ts';
|
|
11
|
+
|
|
12
|
+
const PointImpl = toConcrete<Point>();
|
|
13
13
|
|
|
14
14
|
interface Alias {
|
|
15
15
|
alias: string;
|
|
@@ -269,7 +269,7 @@ export abstract class SQLDialect implements DialectState {
|
|
|
269
269
|
* Delete query and return count removed
|
|
270
270
|
*/
|
|
271
271
|
async deleteAndGetCount<T>(cls: Class<T>, query: Query<T>): Promise<number> {
|
|
272
|
-
const { count } = await this.executeSQL<T>(this.getDeleteSQL(
|
|
272
|
+
const { count } = await this.executeSQL<T>(this.getDeleteSQL(SQLModelUtil.classToStack(cls), query.where));
|
|
273
273
|
return count;
|
|
274
274
|
}
|
|
275
275
|
|
|
@@ -307,7 +307,7 @@ export abstract class SQLDialect implements DialectState {
|
|
|
307
307
|
* Determine table/field namespace for a given stack location
|
|
308
308
|
*/
|
|
309
309
|
namespace(stack: VisitStack[]): string {
|
|
310
|
-
return `${this.ns}${
|
|
310
|
+
return `${this.ns}${SQLModelUtil.buildTable(stack)}`;
|
|
311
311
|
}
|
|
312
312
|
|
|
313
313
|
/**
|
|
@@ -358,7 +358,7 @@ export abstract class SQLDialect implements DialectState {
|
|
|
358
358
|
const clauses = new Map<string, Alias>();
|
|
359
359
|
let idx = 0;
|
|
360
360
|
|
|
361
|
-
|
|
361
|
+
SQLModelUtil.visitSchemaSync(SchemaRegistry.get(cls), {
|
|
362
362
|
onRoot: ({ descend, path }) => {
|
|
363
363
|
const table = resolve(path);
|
|
364
364
|
clauses.set(table, { alias: this.rootAlias, path });
|
|
@@ -396,7 +396,7 @@ export abstract class SQLDialect implements DialectState {
|
|
|
396
396
|
*/
|
|
397
397
|
getWhereFieldSQL(stack: VisitStack[], o: Record<string, unknown>): string {
|
|
398
398
|
const items = [];
|
|
399
|
-
const { foreignMap, localMap } =
|
|
399
|
+
const { foreignMap, localMap } = SQLModelUtil.getFieldsByLocation(stack);
|
|
400
400
|
const SQL_OPS = this.SQL_OPS;
|
|
401
401
|
|
|
402
402
|
for (const key of Object.keys(o)) {
|
|
@@ -533,7 +533,7 @@ export abstract class SQLDialect implements DialectState {
|
|
|
533
533
|
} else if (ModelQueryUtil.has$Not(o)) {
|
|
534
534
|
return `${SQL_OPS.$not} (${this.getWhereGroupingSQL<T>(cls, o.$not)})`;
|
|
535
535
|
} else {
|
|
536
|
-
return this.getWhereFieldSQL(
|
|
536
|
+
return this.getWhereFieldSQL(SQLModelUtil.classToStack(cls), o);
|
|
537
537
|
}
|
|
538
538
|
}
|
|
539
539
|
|
|
@@ -552,7 +552,7 @@ export abstract class SQLDialect implements DialectState {
|
|
|
552
552
|
getOrderBySQL<T>(cls: Class<T>, sortBy?: SortClause<T>[]): string {
|
|
553
553
|
return !sortBy ?
|
|
554
554
|
'' :
|
|
555
|
-
`ORDER BY ${
|
|
555
|
+
`ORDER BY ${SQLModelUtil.orderBy(cls, sortBy).map((ob) =>
|
|
556
556
|
`${this.resolveName(ob.stack)} ${ob.asc ? 'ASC' : 'DESC'}`
|
|
557
557
|
).join(', ')}`;
|
|
558
558
|
}
|
|
@@ -561,8 +561,8 @@ export abstract class SQLDialect implements DialectState {
|
|
|
561
561
|
* Generate SELECT clause
|
|
562
562
|
*/
|
|
563
563
|
getSelectSQL<T>(cls: Class<T>, select?: SelectClause<T>): string {
|
|
564
|
-
const stack =
|
|
565
|
-
const columns = select &&
|
|
564
|
+
const stack = SQLModelUtil.classToStack(cls);
|
|
565
|
+
const columns = select && SQLModelUtil.select(cls, select).map((sel) => this.resolveName([...stack, sel]));
|
|
566
566
|
if (columns) {
|
|
567
567
|
columns.unshift(this.alias(this.pathField));
|
|
568
568
|
}
|
|
@@ -575,10 +575,10 @@ export abstract class SQLDialect implements DialectState {
|
|
|
575
575
|
* Generate FROM clause
|
|
576
576
|
*/
|
|
577
577
|
getFromSQL<T>(cls: Class<T>): string {
|
|
578
|
-
const stack =
|
|
578
|
+
const stack = SQLModelUtil.classToStack(cls);
|
|
579
579
|
const aliases = this.getAliasCache(stack, this.namespace);
|
|
580
|
-
const tables = [...aliases.keys()].
|
|
581
|
-
return `FROM ${tables.map((table
|
|
580
|
+
const tables = [...aliases.keys()].toSorted((a, b) => a.length - b.length); // Shortest first
|
|
581
|
+
return `FROM ${tables.map((table) => {
|
|
582
582
|
const { alias, path } = aliases.get(table)!;
|
|
583
583
|
let from = `${this.ident(table)} ${alias}`;
|
|
584
584
|
if (path.length > 1) {
|
|
@@ -605,25 +605,26 @@ LEFT OUTER JOIN ${from} ON
|
|
|
605
605
|
/**
|
|
606
606
|
* Generate GROUP BY clause
|
|
607
607
|
*/
|
|
608
|
-
getGroupBySQL<T>(cls: Class<T>, query
|
|
609
|
-
|
|
608
|
+
getGroupBySQL<T>(cls: Class<T>, query: Query<T>): string {
|
|
609
|
+
const sortFields = !query.sort ?
|
|
610
|
+
'' :
|
|
611
|
+
SQLModelUtil.orderBy(cls, query.sort)
|
|
612
|
+
.map(x => this.resolveName(x.stack))
|
|
613
|
+
.join(', ');
|
|
614
|
+
|
|
615
|
+
// TODO: Really confused on this
|
|
616
|
+
return `GROUP BY ${this.alias(this.idField)}${sortFields ? `, ${sortFields}` : ''}`;
|
|
610
617
|
}
|
|
611
618
|
|
|
612
619
|
/**
|
|
613
620
|
* Generate full query
|
|
614
621
|
*/
|
|
615
622
|
getQuerySQL<T>(cls: Class<T>, query: Query<T>, where?: WhereClause<T>): string {
|
|
616
|
-
const sortFields = !query.sort ?
|
|
617
|
-
'' :
|
|
618
|
-
SQLUtil.orderBy(cls, query.sort)
|
|
619
|
-
.map(x => this.resolveName(x.stack))
|
|
620
|
-
.join(', ');
|
|
621
|
-
|
|
622
623
|
return `
|
|
623
624
|
${this.getSelectSQL(cls, query.select)}
|
|
624
625
|
${this.getFromSQL(cls)}
|
|
625
626
|
${this.getWhereSQL(cls, where)}
|
|
626
|
-
${this.getGroupBySQL(cls, query)}
|
|
627
|
+
${this.getGroupBySQL(cls, query)}
|
|
627
628
|
${this.getOrderBySQL(cls, query.sort)}
|
|
628
629
|
${this.getLimitSQL(cls, query)}`;
|
|
629
630
|
}
|
|
@@ -633,7 +634,7 @@ ${this.getLimitSQL(cls, query)}`;
|
|
|
633
634
|
const parent = stack.length > 1;
|
|
634
635
|
const array = parent && config.array;
|
|
635
636
|
const fields = SchemaRegistry.has(config.type) ?
|
|
636
|
-
[...
|
|
637
|
+
[...SQLModelUtil.getFieldsByLocation(stack).local] :
|
|
637
638
|
(array ? [castTo<FieldConfig>(config)] : []);
|
|
638
639
|
|
|
639
640
|
if (!parent) {
|
|
@@ -687,7 +688,7 @@ CREATE TABLE IF NOT EXISTS ${this.table(stack)} (
|
|
|
687
688
|
*/
|
|
688
689
|
getCreateAllTablesSQL(cls: Class): string[] {
|
|
689
690
|
const out: string[] = [];
|
|
690
|
-
|
|
691
|
+
SQLModelUtil.visitSchemaSync(SchemaRegistry.get(cls), {
|
|
691
692
|
onRoot: ({ path, descend }) => { out.push(this.getCreateTableSQL(path)); descend(); },
|
|
692
693
|
onSub: ({ path, descend }) => { out.push(this.getCreateTableSQL(path)); descend(); },
|
|
693
694
|
onSimple: ({ path }) => out.push(this.getCreateTableSQL(path))
|
|
@@ -706,7 +707,7 @@ CREATE TABLE IF NOT EXISTS ${this.table(stack)} (
|
|
|
706
707
|
* Get CREATE INDEX sql
|
|
707
708
|
*/
|
|
708
709
|
getCreateIndexSQL<T extends ModelType>(cls: Class<T>, idx: IndexConfig<T>): string {
|
|
709
|
-
const table = this.namespace(
|
|
710
|
+
const table = this.namespace(SQLModelUtil.classToStack(cls));
|
|
710
711
|
const fields: [string, boolean][] = idx.fields.map(x => {
|
|
711
712
|
const key = TypedObject.keys(x)[0];
|
|
712
713
|
const val = x[key];
|
|
@@ -726,7 +727,7 @@ CREATE TABLE IF NOT EXISTS ${this.table(stack)} (
|
|
|
726
727
|
*/
|
|
727
728
|
getDropAllTablesSQL<T extends ModelType>(cls: Class<T>): string[] {
|
|
728
729
|
const out: string[] = [];
|
|
729
|
-
|
|
730
|
+
SQLModelUtil.visitSchemaSync(SchemaRegistry.get(cls), {
|
|
730
731
|
onRoot: ({ path, descend }) => { descend(); out.push(this.getDropTableSQL(path)); },
|
|
731
732
|
onSub: ({ path, descend }) => { descend(); out.push(this.getDropTableSQL(path)); },
|
|
732
733
|
onSimple: ({ path }) => out.push(this.getDropTableSQL(path))
|
|
@@ -739,7 +740,7 @@ CREATE TABLE IF NOT EXISTS ${this.table(stack)} (
|
|
|
739
740
|
*/
|
|
740
741
|
getTruncateAllTablesSQL<T extends ModelType>(cls: Class<T>): string[] {
|
|
741
742
|
const out: string[] = [];
|
|
742
|
-
|
|
743
|
+
SQLModelUtil.visitSchemaSync(SchemaRegistry.get(cls), {
|
|
743
744
|
onRoot: ({ path, descend }) => { descend(); out.push(this.getTruncateTableSQL(path)); },
|
|
744
745
|
onSub: ({ path, descend }) => { descend(); out.push(this.getTruncateTableSQL(path)); },
|
|
745
746
|
onSimple: ({ path }) => out.push(this.getTruncateTableSQL(path))
|
|
@@ -752,9 +753,9 @@ CREATE TABLE IF NOT EXISTS ${this.table(stack)} (
|
|
|
752
753
|
*/
|
|
753
754
|
getInsertSQL(stack: VisitStack[], instances: InsertWrapper['records']): string | undefined {
|
|
754
755
|
const config = stack[stack.length - 1];
|
|
755
|
-
const columns =
|
|
756
|
+
const columns = SQLModelUtil.getFieldsByLocation(stack).local
|
|
756
757
|
.filter(x => !SchemaRegistry.has(x.type))
|
|
757
|
-
.
|
|
758
|
+
.toSorted((a, b) => a.name.localeCompare(b.name));
|
|
758
759
|
const columnNames = columns.map(c => c.name);
|
|
759
760
|
|
|
760
761
|
const hasParent = stack.length > 1;
|
|
@@ -801,13 +802,13 @@ CREATE TABLE IF NOT EXISTS ${this.table(stack)} (
|
|
|
801
802
|
for (let i = 0; i < matrix.length; i++) {
|
|
802
803
|
const { stack: elStack } = instances[i];
|
|
803
804
|
if (hasParent) {
|
|
804
|
-
matrix[i].push(this.hash(`${
|
|
805
|
-
matrix[i].push(this.hash(
|
|
805
|
+
matrix[i].push(this.hash(`${SQLModelUtil.buildPath(elStack)}${isArray ? `[${i + idx}]` : ''}`));
|
|
806
|
+
matrix[i].push(this.hash(SQLModelUtil.buildPath(elStack.slice(0, elStack.length - 1))));
|
|
806
807
|
if (isArray) {
|
|
807
808
|
matrix[i].push(this.resolveValue(this.idxField, i + idx));
|
|
808
809
|
}
|
|
809
810
|
} else {
|
|
810
|
-
matrix[i].push(this.hash(
|
|
811
|
+
matrix[i].push(this.hash(SQLModelUtil.buildPath(elStack)));
|
|
811
812
|
}
|
|
812
813
|
}
|
|
813
814
|
|
|
@@ -823,7 +824,7 @@ ${matrix.map(row => `(${row.join(', ')})`).join(',\n')};`;
|
|
|
823
824
|
getAllInsertSQL<T extends ModelType>(cls: Class<T>, instance: T): string[] {
|
|
824
825
|
const out: string[] = [];
|
|
825
826
|
const add = (text?: string): void => { text && out.push(text); };
|
|
826
|
-
|
|
827
|
+
SQLModelUtil.visitSchemaInstance(cls, instance, {
|
|
827
828
|
onRoot: ({ value, path }) => add(this.getInsertSQL(path, [{ stack: path, value }])),
|
|
828
829
|
onSub: ({ value, path }) => add(this.getInsertSQL(path, [{ stack: path, value }])),
|
|
829
830
|
onSimple: ({ value, path }) => add(this.getInsertSQL(path, [{ stack: path, value }]))
|
|
@@ -836,7 +837,7 @@ ${matrix.map(row => `(${row.join(', ')})`).join(',\n')};`;
|
|
|
836
837
|
*/
|
|
837
838
|
getUpdateSQL(stack: VisitStack[], data: Record<string, unknown>, where?: WhereClause<unknown>): string {
|
|
838
839
|
const { type } = stack[stack.length - 1];
|
|
839
|
-
const { localMap } =
|
|
840
|
+
const { localMap } = SQLModelUtil.getFieldsByLocation(stack);
|
|
840
841
|
return `
|
|
841
842
|
UPDATE ${this.table(stack)} ${this.rootAlias}
|
|
842
843
|
SET
|
|
@@ -888,9 +889,9 @@ ${this.getWhereSQL(cls, where!)}`;
|
|
|
888
889
|
const selectStack: (SelectClause<T> | undefined)[] = [];
|
|
889
890
|
|
|
890
891
|
const buildSet = (children: unknown[], field?: FieldConfig): Record<string, unknown> =>
|
|
891
|
-
|
|
892
|
+
SQLModelUtil.collectDependents(this, stack[stack.length - 1], children, field);
|
|
892
893
|
|
|
893
|
-
await
|
|
894
|
+
await SQLModelUtil.visitSchema(SchemaRegistry.get(cls), {
|
|
894
895
|
onRoot: async (config) => {
|
|
895
896
|
const res = buildSet(items); // Already filtered by initial select query
|
|
896
897
|
selectStack.push(select);
|
package/src/internal/types.ts
CHANGED
package/src/service.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
2
|
ModelType,
|
|
3
3
|
BulkOp, BulkResponse, ModelCrudSupport, ModelStorageSupport, ModelBulkSupport,
|
|
4
|
-
NotFoundError, ModelRegistry, ExistsError, OptionalId,
|
|
5
|
-
|
|
4
|
+
NotFoundError, ModelRegistry, ExistsError, OptionalId, ModelIdSource,
|
|
5
|
+
ModelExpiryUtil, ModelCrudUtil, ModelStorageUtil, ModelBulkUtil,
|
|
6
6
|
} from '@travetto/model';
|
|
7
7
|
import { castTo, Class } from '@travetto/runtime';
|
|
8
8
|
import { DataUtil, SchemaChange } from '@travetto/schema';
|
|
@@ -10,25 +10,18 @@ import { AsyncContext } from '@travetto/context';
|
|
|
10
10
|
import { Injectable } from '@travetto/di';
|
|
11
11
|
import {
|
|
12
12
|
ModelQuery, ModelQueryCrudSupport, ModelQueryFacetSupport, ModelQuerySupport,
|
|
13
|
-
PageableModelQuery, ValidStringFields, WhereClauseRaw, QueryVerifier
|
|
13
|
+
PageableModelQuery, ValidStringFields, WhereClauseRaw, QueryVerifier, ModelQuerySuggestSupport,
|
|
14
|
+
ModelQueryUtil, ModelQuerySuggestUtil, ModelQueryCrudUtil,
|
|
15
|
+
ModelQueryFacet,
|
|
14
16
|
} from '@travetto/model-query';
|
|
15
17
|
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
19
|
-
import {
|
|
20
|
-
import {
|
|
21
|
-
import {
|
|
22
|
-
import {
|
|
23
|
-
import { ModelBulkUtil } from '@travetto/model/src/internal/service/bulk';
|
|
24
|
-
|
|
25
|
-
import { SQLModelConfig } from './config';
|
|
26
|
-
import { Connected, ConnectedIterator, Transactional } from './connection/decorator';
|
|
27
|
-
import { SQLUtil } from './internal/util';
|
|
28
|
-
import { SQLDialect } from './dialect/base';
|
|
29
|
-
import { TableManager } from './table-manager';
|
|
30
|
-
import { Connection } from './connection/base';
|
|
31
|
-
import { InsertWrapper } from './internal/types';
|
|
18
|
+
import { SQLModelConfig } from './config.ts';
|
|
19
|
+
import { Connected, ConnectedIterator, Transactional } from './connection/decorator.ts';
|
|
20
|
+
import { SQLModelUtil } from './util.ts';
|
|
21
|
+
import { SQLDialect } from './dialect/base.ts';
|
|
22
|
+
import { TableManager } from './table-manager.ts';
|
|
23
|
+
import { Connection } from './connection/base.ts';
|
|
24
|
+
import { InsertWrapper } from './internal/types.ts';
|
|
32
25
|
|
|
33
26
|
/**
|
|
34
27
|
* Core for SQL Model Source. Should not have any direct queries,
|
|
@@ -75,7 +68,7 @@ export class SQLModelService implements
|
|
|
75
68
|
const all = toCheck.size ?
|
|
76
69
|
(await this.#exec<ModelType>(
|
|
77
70
|
this.#dialect.getSelectRowsByIdsSQL(
|
|
78
|
-
|
|
71
|
+
SQLModelUtil.classToStack(cls), [...toCheck.keys()], [this.#dialect.idField]
|
|
79
72
|
)
|
|
80
73
|
)).records : [];
|
|
81
74
|
|
|
@@ -190,9 +183,9 @@ export class SQLModelService implements
|
|
|
190
183
|
|
|
191
184
|
@Connected()
|
|
192
185
|
async get<T extends ModelType>(cls: Class<T>, id: string): Promise<T> {
|
|
193
|
-
const
|
|
194
|
-
if (
|
|
195
|
-
return await ModelCrudUtil.load(cls,
|
|
186
|
+
const result = await this.query(cls, { where: castTo({ id }) });
|
|
187
|
+
if (result.length === 1) {
|
|
188
|
+
return await ModelCrudUtil.load(cls, result[0]);
|
|
196
189
|
}
|
|
197
190
|
throw new NotFoundError(cls, id);
|
|
198
191
|
}
|
|
@@ -225,9 +218,9 @@ export class SQLModelService implements
|
|
|
225
218
|
operations.map(x => x[k]).filter((x): x is Required<BulkOp<T>>[K] => !!x);
|
|
226
219
|
|
|
227
220
|
const getStatements = async (k: keyof BulkOp<T>): Promise<InsertWrapper[]> =>
|
|
228
|
-
(await
|
|
221
|
+
(await SQLModelUtil.getInserts(cls, get(k))).filter(x => !!x.records.length);
|
|
229
222
|
|
|
230
|
-
const deletes = [{ stack:
|
|
223
|
+
const deletes = [{ stack: SQLModelUtil.classToStack(cls), ids: get('delete').map(x => x.id) }].filter(x => !!x.ids.length);
|
|
231
224
|
|
|
232
225
|
const [inserts, upserts, updates] = await Promise.all([
|
|
233
226
|
getStatements('insert'),
|
|
@@ -235,15 +228,15 @@ export class SQLModelService implements
|
|
|
235
228
|
getStatements('update')
|
|
236
229
|
]);
|
|
237
230
|
|
|
238
|
-
const
|
|
239
|
-
|
|
240
|
-
return
|
|
231
|
+
const result = await this.#dialect.bulkProcess(deletes, inserts, upserts, updates);
|
|
232
|
+
result.insertedIds = addedIds;
|
|
233
|
+
return result;
|
|
241
234
|
}
|
|
242
235
|
|
|
243
236
|
// Expiry
|
|
244
237
|
@Transactional()
|
|
245
238
|
async deleteExpired<T extends ModelType>(cls: Class<T>): Promise<number> {
|
|
246
|
-
return
|
|
239
|
+
return ModelQueryCrudUtil.deleteExpired(this, cls);
|
|
247
240
|
}
|
|
248
241
|
|
|
249
242
|
@Connected()
|
|
@@ -254,7 +247,7 @@ export class SQLModelService implements
|
|
|
254
247
|
await this.#dialect.fetchDependents(cls, res, query && query.select);
|
|
255
248
|
}
|
|
256
249
|
|
|
257
|
-
const cleaned =
|
|
250
|
+
const cleaned = SQLModelUtil.cleanResults<T>(this.#dialect, res);
|
|
258
251
|
return await Promise.all(cleaned.map(m => ModelCrudUtil.load(cls, m)));
|
|
259
252
|
}
|
|
260
253
|
|
|
@@ -286,7 +279,7 @@ export class SQLModelService implements
|
|
|
286
279
|
async updatePartialByQuery<T extends ModelType>(cls: Class<T>, query: ModelQuery<T>, data: Partial<T>): Promise<number> {
|
|
287
280
|
await QueryVerifier.verify(cls, query);
|
|
288
281
|
const item = await ModelCrudUtil.prePartialUpdate(cls, data);
|
|
289
|
-
const { count } = await this.#exec(this.#dialect.getUpdateSQL(
|
|
282
|
+
const { count } = await this.#exec(this.#dialect.getUpdateSQL(SQLModelUtil.classToStack(cls), item, ModelQueryUtil.getWhereClause(cls, query.where)));
|
|
290
283
|
return count;
|
|
291
284
|
}
|
|
292
285
|
|
|
@@ -294,7 +287,7 @@ export class SQLModelService implements
|
|
|
294
287
|
@Transactional()
|
|
295
288
|
async deleteByQuery<T extends ModelType>(cls: Class<T>, query: ModelQuery<T>): Promise<number> {
|
|
296
289
|
await QueryVerifier.verify(cls, query);
|
|
297
|
-
const { count } = await this.#exec(this.#dialect.getDeleteSQL(
|
|
290
|
+
const { count } = await this.#exec(this.#dialect.getDeleteSQL(SQLModelUtil.classToStack(cls), ModelQueryUtil.getWhereClause(cls, query.where, false)));
|
|
298
291
|
return count;
|
|
299
292
|
}
|
|
300
293
|
|
|
@@ -317,7 +310,7 @@ export class SQLModelService implements
|
|
|
317
310
|
}
|
|
318
311
|
|
|
319
312
|
@Connected()
|
|
320
|
-
async facet<T extends ModelType>(cls: Class<T>, field: ValidStringFields<T>, query?: ModelQuery<T>): Promise<
|
|
313
|
+
async facet<T extends ModelType>(cls: Class<T>, field: ValidStringFields<T>, query?: ModelQuery<T>): Promise<ModelQueryFacet[]> {
|
|
321
314
|
await QueryVerifier.verify(cls, query);
|
|
322
315
|
const col = this.#dialect.ident(field);
|
|
323
316
|
const ttl = this.#dialect.ident('count');
|
package/src/table-manager.ts
CHANGED
|
@@ -4,10 +4,10 @@ import { Class } from '@travetto/runtime';
|
|
|
4
4
|
import { ChangeEvent } from '@travetto/registry';
|
|
5
5
|
import { SchemaChange } from '@travetto/schema';
|
|
6
6
|
|
|
7
|
-
import { Connected, Transactional } from './connection/decorator';
|
|
8
|
-
import { SQLDialect } from './dialect/base';
|
|
9
|
-
import {
|
|
10
|
-
import { Connection } from './connection/base';
|
|
7
|
+
import { Connected, Transactional } from './connection/decorator.ts';
|
|
8
|
+
import { SQLDialect } from './dialect/base.ts';
|
|
9
|
+
import { SQLModelUtil, VisitStack } from './util.ts';
|
|
10
|
+
import { Connection } from './connection/base.ts';
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
13
|
* Manage creation/updating of all tables
|
|
@@ -109,7 +109,7 @@ export class TableManager {
|
|
|
109
109
|
@Connected()
|
|
110
110
|
async changeSchema(cls: Class, change: SchemaChange): Promise<void> {
|
|
111
111
|
try {
|
|
112
|
-
const rootStack =
|
|
112
|
+
const rootStack = SQLModelUtil.classToStack(cls);
|
|
113
113
|
|
|
114
114
|
const changes = change.subs.reduce<Record<ChangeEvent<unknown>['type'], VisitStack[][]>>((acc, v) => {
|
|
115
115
|
const path = v.path.map(f => ({ ...f }));
|
package/src/types.ts
ADDED
|
@@ -2,19 +2,9 @@ import { castKey, castTo, Class, TypedObject } from '@travetto/runtime';
|
|
|
2
2
|
import { SelectClause, SortClause } from '@travetto/model-query';
|
|
3
3
|
import { ModelRegistry, ModelType, OptionalId } from '@travetto/model';
|
|
4
4
|
import { SchemaRegistry, ClassConfig, FieldConfig, DataUtil } from '@travetto/schema';
|
|
5
|
-
import { AllViewSymbol } from '@travetto/schema/src/internal/types';
|
|
6
5
|
|
|
7
|
-
import { DialectState, InsertWrapper, VisitHandler, VisitState, VisitInstanceNode, OrderBy } from './types';
|
|
8
|
-
|
|
9
|
-
const TableSymbol = Symbol.for('@travetto/model-sql:table');
|
|
10
|
-
|
|
11
|
-
export type VisitStack = {
|
|
12
|
-
[TableSymbol]?: string;
|
|
13
|
-
array?: boolean;
|
|
14
|
-
type: Class;
|
|
15
|
-
name: string;
|
|
16
|
-
index?: number;
|
|
17
|
-
};
|
|
6
|
+
import { DialectState, InsertWrapper, VisitHandler, VisitState, VisitInstanceNode, OrderBy } from './internal/types.ts';
|
|
7
|
+
import { TableSymbol, VisitStack } from './types.ts';
|
|
18
8
|
|
|
19
9
|
type FieldCacheEntry = {
|
|
20
10
|
local: FieldConfig[];
|
|
@@ -26,7 +16,7 @@ type FieldCacheEntry = {
|
|
|
26
16
|
/**
|
|
27
17
|
* Utilities for dealing with SQL operations
|
|
28
18
|
*/
|
|
29
|
-
export class
|
|
19
|
+
export class SQLModelUtil {
|
|
30
20
|
|
|
31
21
|
static SCHEMA_FIELDS_CACHE = new Map<Class, FieldCacheEntry>();
|
|
32
22
|
|
|
@@ -79,14 +69,14 @@ export class SQLUtil {
|
|
|
79
69
|
}
|
|
80
70
|
|
|
81
71
|
const model = ModelRegistry.get(cls.class)!;
|
|
82
|
-
const conf = cls.
|
|
72
|
+
const conf = cls.totalView;
|
|
83
73
|
const fields = conf.fields.map(x => ({ ...conf.schema[x] }));
|
|
84
74
|
|
|
85
75
|
// Polymorphic
|
|
86
76
|
if (model && (model.baseType ?? model.subType)) {
|
|
87
77
|
const fieldMap = new Set(fields.map(f => f.name));
|
|
88
78
|
for (const type of ModelRegistry.getClassesByBaseType(ModelRegistry.getBaseModel(cls.class))) {
|
|
89
|
-
const typeConf = SchemaRegistry.get(type).
|
|
79
|
+
const typeConf = SchemaRegistry.get(type).totalView;
|
|
90
80
|
for (const f of typeConf.fields) {
|
|
91
81
|
if (!fieldMap.has(f)) {
|
|
92
82
|
fieldMap.add(f);
|
|
@@ -232,7 +222,7 @@ export class SQLUtil {
|
|
|
232
222
|
if (typeof k === 'string' && !DataUtil.isPlainObject(select[k]) && localMap[k]) {
|
|
233
223
|
if (!v) {
|
|
234
224
|
if (toGet.size === 0) {
|
|
235
|
-
toGet = new Set(SchemaRegistry.get(cls).
|
|
225
|
+
toGet = new Set(SchemaRegistry.get(cls).totalView.fields);
|
|
236
226
|
}
|
|
237
227
|
toGet.delete(k);
|
|
238
228
|
} else {
|
|
@@ -254,7 +244,7 @@ export class SQLUtil {
|
|
|
254
244
|
while (!found) {
|
|
255
245
|
const key = Object.keys(cl)[0];
|
|
256
246
|
const val = cl[key];
|
|
257
|
-
const field = { ...schema.
|
|
247
|
+
const field = { ...schema.totalView.schema[key] };
|
|
258
248
|
if (DataUtil.isPrimitive(val)) {
|
|
259
249
|
stack.push(field);
|
|
260
250
|
found = { stack, asc: val === 1 };
|
|
@@ -315,7 +305,7 @@ export class SQLUtil {
|
|
|
315
305
|
* Build property path for a table/field given the current stack
|
|
316
306
|
*/
|
|
317
307
|
static buildPath(list: VisitStack[]): string {
|
|
318
|
-
return list.map((el
|
|
308
|
+
return list.map((el) => `${el.name}${el.index ? `[${el.index}]` : ''}`).join('.');
|
|
319
309
|
}
|
|
320
310
|
|
|
321
311
|
/**
|
|
@@ -338,7 +328,7 @@ export class SQLUtil {
|
|
|
338
328
|
|
|
339
329
|
await Promise.all(all);
|
|
340
330
|
|
|
341
|
-
const
|
|
342
|
-
return
|
|
331
|
+
const result = [...Object.values(ins)].toSorted((a, b) => a.stack.length - b.stack.length);
|
|
332
|
+
return result;
|
|
343
333
|
}
|
|
344
334
|
}
|
package/support/test/query.ts
CHANGED
|
@@ -3,10 +3,10 @@ import assert from 'node:assert';
|
|
|
3
3
|
import { Schema, FieldConfig } from '@travetto/schema';
|
|
4
4
|
import { Suite, Test } from '@travetto/test';
|
|
5
5
|
import { castTo } from '@travetto/runtime';
|
|
6
|
-
import { BaseModelSuite } from '@travetto/model/support/test/base';
|
|
6
|
+
import { BaseModelSuite } from '@travetto/model/support/test/base.ts';
|
|
7
7
|
|
|
8
|
-
import { VisitStack } from '../../src/
|
|
9
|
-
import { SQLModelService } from '../../src/service';
|
|
8
|
+
import { VisitStack } from '../../src/types.ts';
|
|
9
|
+
import { SQLModelService } from '../../src/service.ts';
|
|
10
10
|
|
|
11
11
|
@Schema()
|
|
12
12
|
class User {
|