uql-orm 0.21.0 → 0.23.0
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/README.md +5 -4
- package/dist/browser/uql-browser.min.js.map +3 -3
- package/dist/bunSql/bunSqlCockroachDialect.d.ts +2 -2
- package/dist/bunSql/bunSqlCockroachDialect.js +4 -10
- package/dist/bunSql/bunSqlPostgresDialect.d.ts +2 -2
- package/dist/bunSql/bunSqlPostgresDialect.js +4 -10
- package/dist/cockroachdb/cockroachDialect.d.ts +20 -4
- package/dist/cockroachdb/cockroachDialect.js +33 -18
- package/dist/d1/d1Querier.d.ts +4 -3
- package/dist/d1/d1Querier.js +0 -3
- package/dist/d1/d1SqliteDialect.d.ts +7 -0
- package/dist/d1/d1SqliteDialect.js +9 -0
- package/dist/dialect/abstractDialect.d.ts +8 -3
- package/dist/dialect/abstractDialect.js +9 -4
- package/dist/dialect/abstractSqlDialect.d.ts +49 -8
- package/dist/dialect/abstractSqlDialect.js +95 -47
- package/dist/dialect/index.d.ts +1 -0
- package/dist/dialect/index.js +1 -0
- package/dist/dialect/indexSqlDialect.d.ts +57 -0
- package/dist/dialect/indexSqlDialect.js +101 -0
- package/dist/dialect/mysqlLikeSqlDialect.d.ts +26 -1
- package/dist/dialect/mysqlLikeSqlDialect.js +57 -1
- package/dist/dialect/pgLikeSqlDialect.d.ts +37 -22
- package/dist/dialect/pgLikeSqlDialect.js +97 -68
- package/dist/dialect/vectorCast.d.ts +21 -0
- package/dist/dialect/vectorCast.js +28 -0
- package/dist/dialect/vectorSqlDialect.d.ts +10 -3
- package/dist/dialect/vectorSqlDialect.js +19 -8
- package/dist/entity/decorator/bag.d.ts +35 -0
- package/dist/entity/decorator/bag.js +54 -0
- package/dist/entity/decorator/entity.d.ts +28 -2
- package/dist/entity/decorator/entity.js +40 -2
- package/dist/entity/decorator/members.d.ts +51 -0
- package/dist/entity/decorator/members.js +51 -0
- package/dist/entity/index.d.ts +3 -1
- package/dist/entity/index.js +3 -1
- package/dist/entity/metadata/definition.d.ts +23 -8
- package/dist/entity/metadata/definition.js +56 -100
- package/dist/http/contract.js +1 -2
- package/dist/http/handler.js +5 -26
- package/dist/http/query.js +1 -1
- package/dist/libsql/libsqlDialect.d.ts +12 -2
- package/dist/libsql/libsqlDialect.js +14 -2
- package/dist/libsql/libsqlQuerier.d.ts +9 -22
- package/dist/libsql/libsqlQuerier.js +9 -85
- package/dist/libsql/libsqlQuerierPool.d.ts +10 -8
- package/dist/libsql/libsqlQuerierPool.js +17 -15
- package/dist/maria/mariaDialect.d.ts +26 -4
- package/dist/maria/mariaDialect.js +54 -15
- package/dist/migrate/acquireQuerierForMigrations.d.ts +13 -1
- package/dist/migrate/acquireQuerierForMigrations.js +28 -0
- package/dist/migrate/builder/migrationBuilder.d.ts +9 -29
- package/dist/migrate/builder/migrationBuilder.js +58 -76
- package/dist/migrate/builder/splitSqlStatements.d.ts +0 -2
- package/dist/migrate/builder/splitSqlStatements.js +0 -2
- package/dist/migrate/builder/tableBuilder.d.ts +12 -2
- package/dist/migrate/builder/tableBuilder.js +71 -119
- package/dist/migrate/builder/types.d.ts +30 -41
- package/dist/migrate/cli-config.js +24 -8
- package/dist/migrate/cli.d.ts +3 -2
- package/dist/migrate/cli.js +26 -49
- package/dist/migrate/codegen/entityCodeGenerator.d.ts +0 -4
- package/dist/migrate/codegen/entityCodeGenerator.js +8 -67
- package/dist/migrate/codegen/fieldOptionsSource.d.ts +10 -0
- package/dist/migrate/codegen/fieldOptionsSource.js +55 -0
- package/dist/migrate/codegen/index.d.ts +0 -3
- package/dist/migrate/codegen/index.js +0 -6
- package/dist/migrate/drift/driftDetector.d.ts +10 -1
- package/dist/migrate/drift/driftDetector.js +22 -5
- package/dist/migrate/generator/indexNodeToSchema.d.ts +9 -0
- package/dist/migrate/generator/indexNodeToSchema.js +14 -0
- package/dist/migrate/generator/mongoCommand.d.ts +57 -0
- package/dist/migrate/generator/mongoCommand.js +26 -0
- package/dist/migrate/generator/mongoSchemaGenerator.d.ts +9 -20
- package/dist/migrate/generator/mongoSchemaGenerator.js +40 -82
- package/dist/migrate/index.d.ts +0 -1
- package/dist/migrate/index.js +0 -1
- package/dist/migrate/introspection/abstractSqlSchemaIntrospector.d.ts +30 -11
- package/dist/migrate/introspection/abstractSqlSchemaIntrospector.js +61 -57
- package/dist/migrate/introspection/baseSqlIntrospector.d.ts +0 -1
- package/dist/migrate/introspection/baseSqlIntrospector.js +3 -12
- package/dist/migrate/introspection/mongoIntrospector.js +9 -15
- package/dist/migrate/introspection/mysqlIntrospector.d.ts +5 -8
- package/dist/migrate/introspection/mysqlIntrospector.js +4 -10
- package/dist/migrate/introspection/postgresIntrospector.d.ts +5 -8
- package/dist/migrate/introspection/postgresIntrospector.js +4 -10
- package/dist/migrate/introspection/sqliteIntrospector.d.ts +6 -5
- package/dist/migrate/introspection/sqliteIntrospector.js +26 -18
- package/dist/migrate/migrator.d.ts +23 -3
- package/dist/migrate/migrator.js +101 -150
- package/dist/migrate/schemaGenerator.d.ts +26 -37
- package/dist/migrate/schemaGenerator.js +69 -174
- package/dist/migrate/schemaGeneratorAsync.d.ts +2 -2
- package/dist/migrate/schemaGeneratorAsync.js +3 -3
- package/dist/migrate/storage/databaseStorage.d.ts +2 -0
- package/dist/migrate/storage/databaseStorage.js +12 -27
- package/dist/mongo/mongoDialect.d.ts +2 -0
- package/dist/mongo/mongoDialect.js +14 -3
- package/dist/mongo/mongodbQuerier.js +171 -229
- package/dist/mysql/mysqlDialect.d.ts +8 -0
- package/dist/mysql/mysqlDialect.js +13 -0
- package/dist/nestjs/uqlContextInterceptor.js +58 -22
- package/dist/nestjs/uqlModule.d.ts +2 -11
- package/dist/nestjs/uqlModule.js +93 -44
- package/dist/postgres/postgresDialect.js +2 -2
- package/dist/querier/abstractQuerier.d.ts +28 -0
- package/dist/querier/abstractQuerier.js +47 -22
- package/dist/querier/abstractQuerierPool.d.ts +3 -0
- package/dist/querier/abstractQuerierPool.js +5 -3
- package/dist/querier/abstractSqlQuerier.d.ts +1 -3
- package/dist/querier/abstractSqlQuerier.js +48 -96
- package/dist/querier/index.d.ts +3 -1
- package/dist/querier/index.js +3 -1
- package/dist/querier/querierContext.browser.d.ts +12 -0
- package/dist/querier/querierContext.browser.js +18 -0
- package/dist/querier/querierContext.d.ts +22 -0
- package/dist/querier/querierContext.js +42 -0
- package/dist/querier/queryError.d.ts +22 -0
- package/dist/querier/queryError.js +20 -0
- package/dist/querier/transactional.d.ts +26 -0
- package/dist/querier/transactional.js +43 -0
- package/dist/schema/canonicalType.d.ts +14 -2
- package/dist/schema/canonicalType.js +41 -31
- package/dist/schema/index.d.ts +1 -1
- package/dist/schema/schemaASTBuilder.d.ts +9 -2
- package/dist/schema/schemaASTBuilder.js +58 -49
- package/dist/schema/types.d.ts +10 -58
- package/dist/sqlite/abstractSqliteQuerier.d.ts +46 -0
- package/dist/sqlite/abstractSqliteQuerier.js +44 -0
- package/dist/sqlite/bunSqliteAdapter.bun.d.ts +26 -0
- package/dist/sqlite/bunSqliteAdapter.bun.js +25 -0
- package/dist/sqlite/hranaQuerier.d.ts +54 -0
- package/dist/sqlite/hranaQuerier.js +68 -0
- package/dist/sqlite/hranaQuerierPool.d.ts +21 -0
- package/dist/sqlite/hranaQuerierPool.js +25 -0
- package/dist/sqlite/index.d.ts +3 -1
- package/dist/sqlite/index.js +3 -1
- package/dist/sqlite/localSqliteQuerierPool.d.ts +31 -0
- package/dist/sqlite/localSqliteQuerierPool.js +34 -0
- package/dist/sqlite/nodeSqliteAdapter.d.ts +35 -0
- package/dist/sqlite/nodeSqliteAdapter.js +28 -0
- package/dist/sqlite/nodeSqliteQuerierPool.d.ts +28 -0
- package/dist/sqlite/nodeSqliteQuerierPool.js +29 -0
- package/dist/sqlite/sqliteDialect.d.ts +18 -5
- package/dist/sqlite/sqliteDialect.js +23 -36
- package/dist/sqlite/sqliteQuerier.d.ts +16 -9
- package/dist/sqlite/sqliteQuerier.js +4 -27
- package/dist/sqlite/sqliteQuerierPool.d.ts +11 -14
- package/dist/sqlite/sqliteQuerierPool.js +16 -24
- package/dist/turso/index.d.ts +3 -0
- package/dist/turso/index.js +3 -0
- package/dist/turso/local.d.ts +3 -0
- package/dist/turso/local.js +3 -0
- package/dist/turso/tursoDialect.d.ts +15 -0
- package/dist/turso/tursoDialect.js +18 -0
- package/dist/turso/tursoLocalQuerier.d.ts +25 -0
- package/dist/turso/tursoLocalQuerier.js +20 -0
- package/dist/turso/tursoLocalQuerierPool.d.ts +32 -0
- package/dist/turso/tursoLocalQuerierPool.js +39 -0
- package/dist/turso/tursoQuerier.d.ts +10 -0
- package/dist/turso/tursoQuerier.js +10 -0
- package/dist/turso/tursoQuerierPool.d.ts +39 -0
- package/dist/turso/tursoQuerierPool.js +38 -0
- package/dist/type/config.d.ts +0 -6
- package/dist/type/dialect.d.ts +12 -4
- package/dist/type/dialect.js +7 -1
- package/dist/type/entity.d.ts +154 -26
- package/dist/type/migration.d.ts +32 -23
- package/dist/type/querier.d.ts +6 -0
- package/dist/type/vector.d.ts +5 -2
- package/dist/util/hook.util.js +1 -1
- package/dist/util/index.d.ts +1 -0
- package/dist/util/index.js +1 -0
- package/dist/util/indexColumn.util.d.ts +6 -0
- package/dist/util/indexColumn.util.js +26 -0
- package/package.json +21 -16
- package/dist/entity/decorator/field.d.ts +0 -2
- package/dist/entity/decorator/field.js +0 -7
- package/dist/entity/decorator/filter.d.ts +0 -7
- package/dist/entity/decorator/filter.js +0 -11
- package/dist/entity/decorator/hook.d.ts +0 -7
- package/dist/entity/decorator/hook.js +0 -14
- package/dist/entity/decorator/id.d.ts +0 -2
- package/dist/entity/decorator/id.js +0 -7
- package/dist/entity/decorator/index-decorator.d.ts +0 -29
- package/dist/entity/decorator/index-decorator.js +0 -26
- package/dist/entity/decorator/index.d.ts +0 -8
- package/dist/entity/decorator/index.js +0 -8
- package/dist/entity/decorator/relation.d.ts +0 -8
- package/dist/entity/decorator/relation.js +0 -19
- package/dist/migrate/codegen/entityMerger.d.ts +0 -114
- package/dist/migrate/codegen/entityMerger.js +0 -293
- package/dist/migrate/codegen/migrationCodeGenerator.d.ts +0 -61
- package/dist/migrate/codegen/migrationCodeGenerator.js +0 -355
- package/dist/migrate/codegen/smartRelationDetector.d.ts +0 -47
- package/dist/migrate/codegen/smartRelationDetector.js +0 -134
- package/dist/migrate/generator/index.d.ts +0 -2
- package/dist/migrate/generator/index.js +0 -2
- package/dist/migrate/sync/index.d.ts +0 -6
- package/dist/migrate/sync/index.js +0 -6
- package/dist/migrate/sync/schemaSync.d.ts +0 -131
- package/dist/migrate/sync/schemaSync.js +0 -259
- package/dist/querier/decorator/index.d.ts +0 -4
- package/dist/querier/decorator/index.js +0 -4
- package/dist/querier/decorator/injectQuerier.d.ts +0 -3
- package/dist/querier/decorator/injectQuerier.js +0 -33
- package/dist/querier/decorator/log.d.ts +0 -32
- package/dist/querier/decorator/log.js +0 -52
- package/dist/querier/decorator/serialized.d.ts +0 -6
- package/dist/querier/decorator/serialized.js +0 -13
- package/dist/querier/decorator/transactional.d.ts +0 -6
- package/dist/querier/decorator/transactional.js +0 -48
- package/dist/sqlite/betterSqlite3Dialect.d.ts +0 -9
- package/dist/sqlite/betterSqlite3Dialect.js +0 -9
|
@@ -3,6 +3,7 @@ import { QueryRaw, } from '../type/index.js';
|
|
|
3
3
|
import { escapeSingleQuotes } from '../util/sqlLiteral.js';
|
|
4
4
|
import { AbstractSqlDialect } from './abstractSqlDialect.js';
|
|
5
5
|
import { JSON_PULL_ALIAS, jsonSetTarget } from './jsonSql.js';
|
|
6
|
+
import { resolveVectorCast, toSparsevecLiteral } from './vectorCast.js';
|
|
6
7
|
/**
|
|
7
8
|
* Shared AST/quoting/JSONB/full-text-search/vector-search implementation between Postgres and
|
|
8
9
|
* CockroachDB (wire- and SQL-compatible for everything below, including `to_tsvector`/`to_tsquery`
|
|
@@ -24,7 +25,7 @@ export class PgLikeSqlDialect extends AbstractSqlDialect {
|
|
|
24
25
|
renameColumn: true,
|
|
25
26
|
foreignKeyAlter: true,
|
|
26
27
|
columnComment: false,
|
|
27
|
-
|
|
28
|
+
inlineVectorIndex: false,
|
|
28
29
|
vectorSupportsLength: true,
|
|
29
30
|
supportsTimestamptz: true,
|
|
30
31
|
defaultStringAsText: true,
|
|
@@ -42,12 +43,16 @@ export class PgLikeSqlDialect extends AbstractSqlDialect {
|
|
|
42
43
|
alterColumnStrategy = 'separate-clauses';
|
|
43
44
|
insertIdSource = 'returning';
|
|
44
45
|
maxBindValues = 65535;
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
46
|
+
/**
|
|
47
|
+
* Each metric's pgvector distance operator and the operator-class suffix its index takes, in one
|
|
48
|
+
* place so a dialect cannot end up with the operator but not the opclass. The key set is the single
|
|
49
|
+
* source of truth for which metrics the dialect supports at all: CockroachDB narrows it to three.
|
|
50
|
+
*/
|
|
51
|
+
vectorMetrics = new Map([
|
|
52
|
+
['cosine', { op: '<=>', opsSuffix: 'cosine' }],
|
|
53
|
+
['l2', { op: '<->', opsSuffix: 'l2' }],
|
|
54
|
+
['inner', { op: '<#>', opsSuffix: 'ip' }],
|
|
55
|
+
['l1', { op: '<+>', opsSuffix: 'l1' }],
|
|
51
56
|
]);
|
|
52
57
|
normalizeValue(value) {
|
|
53
58
|
if (value != null && typeof value === 'object' && Array.isArray(value)) {
|
|
@@ -55,55 +60,88 @@ export class PgLikeSqlDialect extends AbstractSqlDialect {
|
|
|
55
60
|
}
|
|
56
61
|
return super.normalizeValue(value);
|
|
57
62
|
}
|
|
58
|
-
|
|
59
|
-
|
|
63
|
+
/** pgvector's own index types; CockroachDB's native one widens this. */
|
|
64
|
+
isVectorIndex(index) {
|
|
65
|
+
return index.type === 'hnsw' || index.type === 'ivfflat';
|
|
66
|
+
}
|
|
67
|
+
indexAccessMethod(index) {
|
|
68
|
+
return index.type ? ` USING ${index.type}` : '';
|
|
69
|
+
}
|
|
70
|
+
indexFeatures = new Set([
|
|
71
|
+
'expression',
|
|
72
|
+
'nullsOrder',
|
|
73
|
+
'opsClass',
|
|
74
|
+
'include',
|
|
75
|
+
]);
|
|
76
|
+
/**
|
|
77
|
+
* A vector index's operator class is named `{type}_{metric}_ops`: an index on a `halfvec` column
|
|
78
|
+
* needs `halfvec_cosine_ops`, and `vector_cosine_ops` there is rejected outright. An unsupported
|
|
79
|
+
* distance throws rather than being omitted, since a bare `USING hnsw ("embedding")` would build
|
|
80
|
+
* with the dialect's default metric instead of the one requested, with nothing signalling it.
|
|
81
|
+
* Everything else takes the operator class the entry declares, e.g. `jsonb_path_ops` for GIN.
|
|
82
|
+
*/
|
|
83
|
+
indexColumnOpsClass(entry, index) {
|
|
84
|
+
if (!this.isVectorIndex(index) || !index.distance) {
|
|
85
|
+
return entry.opsClass ? ` ${entry.opsClass}` : '';
|
|
86
|
+
}
|
|
87
|
+
const metric = this.vectorMetrics.get(index.distance);
|
|
88
|
+
if (!metric) {
|
|
89
|
+
throw new TypeError(`${this.dialectName} does not support vector distance metric: ${index.distance} (index "${index.name}")`);
|
|
90
|
+
}
|
|
91
|
+
const vectorType = this.supportedVectorType(index.vectorType ?? 'vector');
|
|
92
|
+
const opsClass = `${vectorType}_${metric.opsSuffix}_ops`;
|
|
93
|
+
// IVFFlat has neither a sparsevec nor an L1 operator class; HNSW has all of them (pgvector 0.8.2).
|
|
94
|
+
if (index.type === 'ivfflat' && (vectorType === 'sparsevec' || index.distance === 'l1')) {
|
|
95
|
+
throw new TypeError(`ivfflat has no ${opsClass} operator class (index "${index.name}"); use hnsw`);
|
|
96
|
+
}
|
|
97
|
+
return ` ${opsClass}`;
|
|
98
|
+
}
|
|
99
|
+
indexInclude(index) {
|
|
100
|
+
return index.include?.length ? ` INCLUDE (${index.include.map((column) => this.escapeId(column)).join(', ')})` : '';
|
|
60
101
|
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
102
|
+
indexTuning(index) {
|
|
103
|
+
if (!this.isVectorIndex(index)) {
|
|
104
|
+
return '';
|
|
105
|
+
}
|
|
106
|
+
const params = [];
|
|
107
|
+
if (index.m !== undefined)
|
|
108
|
+
params.push(`m = ${index.m}`);
|
|
109
|
+
if (index.efConstruction !== undefined)
|
|
110
|
+
params.push(`ef_construction = ${index.efConstruction}`);
|
|
111
|
+
if (index.lists !== undefined)
|
|
112
|
+
params.push(`lists = ${index.lists}`);
|
|
113
|
+
return params.length > 0 ? ` WITH (${params.join(', ')})` : '';
|
|
64
114
|
}
|
|
65
|
-
|
|
66
|
-
|
|
115
|
+
placeholder(index) {
|
|
116
|
+
return `$${index}`;
|
|
67
117
|
}
|
|
68
118
|
/**
|
|
69
|
-
*
|
|
70
|
-
*
|
|
71
|
-
*
|
|
72
|
-
* detect insert-vs-update; CockroachDB has no `xmax`/`ctid` system columns, so it uses the default
|
|
73
|
-
* (empty) and `created` stays `undefined` in the result.
|
|
119
|
+
* The same statement as the base, binding the assignments into the main context instead of a second
|
|
120
|
+
* one: `$N` placeholders carry their own index, so the values need not be in statement order, and
|
|
121
|
+
* computing them first is what keeps `appendInsertValues`' `onInsert` fields out of the update set.
|
|
74
122
|
*/
|
|
75
|
-
|
|
123
|
+
upsert(ctx, entity, conflictPaths, payload, extraReturning = '') {
|
|
76
124
|
const meta = getMeta(entity);
|
|
77
|
-
const update = this.getUpsertUpdateAssignments(ctx, meta, conflictPaths, payload,
|
|
78
|
-
const
|
|
125
|
+
const update = this.getUpsertUpdateAssignments(ctx, meta, conflictPaths, payload, this.upsertExcluded);
|
|
126
|
+
const keys = this.getUpsertConflictPathsStr(meta, conflictPaths);
|
|
79
127
|
const onConflict = update ? `DO UPDATE SET ${update}` : 'DO NOTHING';
|
|
80
|
-
|
|
81
|
-
ctx.append(` ON CONFLICT (${
|
|
128
|
+
this.appendInsertValues(ctx, entity, payload);
|
|
129
|
+
ctx.append(` ON CONFLICT (${keys}) ${onConflict} ${this.returningId(entity)}${extraReturning}`);
|
|
82
130
|
}
|
|
83
131
|
/**
|
|
84
|
-
*
|
|
85
|
-
*
|
|
86
|
-
*
|
|
132
|
+
* `to_tsvector(...) @@ websearch_to_tsquery(...)`. `websearch_to_tsquery` takes free-form user input
|
|
133
|
+
* (quoted phrases, `or`, `-negation`) and never raises a syntax error, unlike `to_tsquery`, which
|
|
134
|
+
* rejects anything unparseable - including a plain two-word search.
|
|
87
135
|
*/
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
})
|
|
98
|
-
.join(` || ' ' || `);
|
|
99
|
-
// The config is bound once and its numbered placeholder reused by both calls.
|
|
100
|
-
const config = search.$config ? `${this.addValue(ctx.values, search.$config)}::regconfig, ` : '';
|
|
101
|
-
ctx.append(`to_tsvector(${config}${fields}) @@ websearch_to_tsquery(${config}`);
|
|
102
|
-
ctx.addValue(search.$value);
|
|
103
|
-
ctx.append(')');
|
|
104
|
-
return;
|
|
105
|
-
}
|
|
106
|
-
super.compare(ctx, entity, key, val, opts);
|
|
136
|
+
appendTextSearch(ctx, _entity, meta, search) {
|
|
137
|
+
const fields = (search.$fields ?? [])
|
|
138
|
+
.map((key) => this.escapeId(this.resolveColumnName(key, meta.fields[key])))
|
|
139
|
+
.join(` || ' ' || `);
|
|
140
|
+
// The config is bound once and its numbered placeholder reused by both calls.
|
|
141
|
+
const config = search.$config ? `${this.addValue(ctx.values, search.$config)}::regconfig, ` : '';
|
|
142
|
+
ctx.append(`to_tsvector(${config}${fields}) @@ websearch_to_tsquery(${config}`);
|
|
143
|
+
ctx.addValue(search.$value);
|
|
144
|
+
ctx.append(')');
|
|
107
145
|
}
|
|
108
146
|
jsonAll(ctx, jsonField, value) {
|
|
109
147
|
return `${jsonField} @> ${this.jsonVal(ctx, value)}`;
|
|
@@ -146,9 +184,14 @@ export class PgLikeSqlDialect extends AbstractSqlDialect {
|
|
|
146
184
|
appendJsonValue(ctx, value, type) {
|
|
147
185
|
ctx.append(this.jsonVal(ctx, value, type));
|
|
148
186
|
}
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
187
|
+
/**
|
|
188
|
+
* pgvector needs the cast to pick the right type, and `sparsevec` needs its own literal: the dense
|
|
189
|
+
* `[1,0,2]` every other type takes is rejected as "invalid input syntax for type sparsevec".
|
|
190
|
+
*/
|
|
191
|
+
appendVectorValue(ctx, value, field) {
|
|
192
|
+
const vectorType = this.supportedVectorType(resolveVectorCast(field));
|
|
193
|
+
ctx.addValue(vectorType === 'sparsevec' ? toSparsevecLiteral(value) : `[${value.join(',')}]`);
|
|
194
|
+
ctx.append(`::${vectorType}`);
|
|
152
195
|
}
|
|
153
196
|
/**
|
|
154
197
|
* `create_if_missing => false` keeps an absent key (and a NULL column) untouched; `WITH
|
|
@@ -189,29 +232,15 @@ export class PgLikeSqlDialect extends AbstractSqlDialect {
|
|
|
189
232
|
const ph = this.addValue(ctx.values, json);
|
|
190
233
|
return this.features.explicitJsonCast ? `(${ph}::text)::${type}` : `${ph}::${type}`;
|
|
191
234
|
}
|
|
192
|
-
/**
|
|
193
|
-
* pgvector distance operators. Not every dialect supports every metric - see
|
|
194
|
-
* {@link CockroachDialect.vectorOpsClass} for which ones each dialect actually has; the
|
|
195
|
-
* `vectorOpsClass` key set (checked in `appendVectorSort` below via `vectorOpsClass.get`)
|
|
196
|
-
* is the single source of truth for that, not this map.
|
|
197
|
-
*/
|
|
198
|
-
static VECTOR_OPS = {
|
|
199
|
-
cosine: '<=>',
|
|
200
|
-
l2: '<->',
|
|
201
|
-
inner: '<#>',
|
|
202
|
-
l1: '<+>',
|
|
203
|
-
hamming: '<~>',
|
|
204
|
-
};
|
|
205
235
|
/** Emit a pgvector-style distance expression: `"col" <op> $N::<vectorType>`. */
|
|
206
236
|
appendVectorSort(ctx, meta, key, search) {
|
|
207
|
-
const { colName, distance,
|
|
208
|
-
|
|
237
|
+
const { colName, distance, field } = this.resolveVectorSortParams(meta, key, search);
|
|
238
|
+
const metric = this.vectorMetrics.get(distance);
|
|
239
|
+
if (!metric) {
|
|
209
240
|
throw new TypeError(`${this.dialectName} does not support vector distance metric: ${distance}`);
|
|
210
241
|
}
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
ctx.addValue(`[${search.$vector.join(',')}]`);
|
|
214
|
-
ctx.append(`::${vectorCast}`);
|
|
242
|
+
ctx.append(`${this.escapeId(colName)} ${metric.op} `);
|
|
243
|
+
this.appendVectorValue(ctx, search.$vector, field);
|
|
215
244
|
}
|
|
216
245
|
}
|
|
217
246
|
/**
|
|
@@ -2,10 +2,31 @@
|
|
|
2
2
|
* Kept out of `schema/canonicalType.ts`: importing one function from that migration/codegen module
|
|
3
3
|
* pulled all ~18 KB of its type-mapping tables into every consumer's bundle.
|
|
4
4
|
*/
|
|
5
|
+
import type { DialectName } from '../type/querier.js';
|
|
5
6
|
/** Vector cast types supported by pgvector. */
|
|
6
7
|
export type VectorCast = 'vector' | 'halfvec' | 'sparsevec';
|
|
8
|
+
/**
|
|
9
|
+
* The dialects that have more than one vector column type. pgvector is the only one: `halfvec` and
|
|
10
|
+
* `sparsevec` exist nowhere else (`HALFVEC(3)` is a syntax error on CockroachDB 26.2 and MariaDB 12.3,
|
|
11
|
+
* not merely unsupported at runtime), so a field declaring one is stored - and therefore cast - as
|
|
12
|
+
* plain `vector`. Both halves of that used to be stated separately, in this dialect layer and in the
|
|
13
|
+
* migration type maps.
|
|
14
|
+
*/
|
|
15
|
+
export declare const MULTI_VECTOR_TYPE_DIALECTS: ReadonlySet<DialectName>;
|
|
16
|
+
/**
|
|
17
|
+
* Whether a declared field type is a vector of any width. Every dialect that treats vectors specially
|
|
18
|
+
* has to answer this for all three, not just `vector`: matching that one alone left `halfvec` and
|
|
19
|
+
* `sparsevec` fields binding as plain arrays on insert and reading back raw.
|
|
20
|
+
*/
|
|
21
|
+
export declare function isVectorFieldType(type: unknown): boolean;
|
|
7
22
|
/** Resolves the effective cast from field options, `columnType` taking priority over `type`. */
|
|
8
23
|
export declare function resolveVectorCast(field: {
|
|
9
24
|
type?: unknown;
|
|
10
25
|
columnType?: unknown;
|
|
11
26
|
} | undefined): VectorCast;
|
|
27
|
+
/**
|
|
28
|
+
* pgvector's `sparsevec` literal: 1-based `index:value` pairs of the non-zero elements, then the
|
|
29
|
+
* dimension count (`{1:1,3:2}/3`). A dense `[1,0,2]` is rejected outright by that type, so an entity
|
|
30
|
+
* declaring `type: 'sparsevec'` still hands UQL the dense array its field type promises.
|
|
31
|
+
*/
|
|
32
|
+
export declare function toSparsevecLiteral(values: readonly unknown[]): string;
|
|
@@ -2,6 +2,22 @@
|
|
|
2
2
|
* Kept out of `schema/canonicalType.ts`: importing one function from that migration/codegen module
|
|
3
3
|
* pulled all ~18 KB of its type-mapping tables into every consumer's bundle.
|
|
4
4
|
*/
|
|
5
|
+
/**
|
|
6
|
+
* The dialects that have more than one vector column type. pgvector is the only one: `halfvec` and
|
|
7
|
+
* `sparsevec` exist nowhere else (`HALFVEC(3)` is a syntax error on CockroachDB 26.2 and MariaDB 12.3,
|
|
8
|
+
* not merely unsupported at runtime), so a field declaring one is stored - and therefore cast - as
|
|
9
|
+
* plain `vector`. Both halves of that used to be stated separately, in this dialect layer and in the
|
|
10
|
+
* migration type maps.
|
|
11
|
+
*/
|
|
12
|
+
export const MULTI_VECTOR_TYPE_DIALECTS = new Set(['postgres']);
|
|
13
|
+
/**
|
|
14
|
+
* Whether a declared field type is a vector of any width. Every dialect that treats vectors specially
|
|
15
|
+
* has to answer this for all three, not just `vector`: matching that one alone left `halfvec` and
|
|
16
|
+
* `sparsevec` fields binding as plain arrays on insert and reading back raw.
|
|
17
|
+
*/
|
|
18
|
+
export function isVectorFieldType(type) {
|
|
19
|
+
return type === 'vector' || type === 'halfvec' || type === 'sparsevec';
|
|
20
|
+
}
|
|
5
21
|
/** Resolves the effective cast from field options, `columnType` taking priority over `type`. */
|
|
6
22
|
export function resolveVectorCast(field) {
|
|
7
23
|
const raw = field?.columnType ?? field?.type;
|
|
@@ -11,3 +27,15 @@ export function resolveVectorCast(field) {
|
|
|
11
27
|
return 'sparsevec';
|
|
12
28
|
return 'vector';
|
|
13
29
|
}
|
|
30
|
+
/**
|
|
31
|
+
* pgvector's `sparsevec` literal: 1-based `index:value` pairs of the non-zero elements, then the
|
|
32
|
+
* dimension count (`{1:1,3:2}/3`). A dense `[1,0,2]` is rejected outright by that type, so an entity
|
|
33
|
+
* declaring `type: 'sparsevec'` still hands UQL the dense array its field type promises.
|
|
34
|
+
*/
|
|
35
|
+
export function toSparsevecLiteral(values) {
|
|
36
|
+
const pairs = values
|
|
37
|
+
.map((value, index) => `${index + 1}:${value}`)
|
|
38
|
+
.filter((_, index) => Number(values[index]) !== 0)
|
|
39
|
+
.join(',');
|
|
40
|
+
return `{${pairs}}/${values.length}`;
|
|
41
|
+
}
|
|
@@ -14,8 +14,6 @@ import { type VectorCast } from './vectorCast.js';
|
|
|
14
14
|
* {@link appendVectorSort} or filling {@link vectorDistanceFns}.
|
|
15
15
|
*/
|
|
16
16
|
export declare abstract class VectorSqlDialect extends AbstractDialect {
|
|
17
|
-
/** Vector index operator classes, keyed by distance metric. Partial: not every dialect supports every metric. */
|
|
18
|
-
readonly vectorOpsClass: ReadonlyMap<VectorDistance, string> | undefined;
|
|
19
17
|
readonly vectorExtension: string | undefined;
|
|
20
18
|
/**
|
|
21
19
|
* Mapping of UQL vector distance metrics to native SQL functions.
|
|
@@ -33,8 +31,17 @@ export declare abstract class VectorSqlDialect extends AbstractDialect {
|
|
|
33
31
|
colName: string;
|
|
34
32
|
distance: VectorDistance;
|
|
35
33
|
field: FieldOptions | undefined;
|
|
36
|
-
vectorCast: VectorCast;
|
|
37
34
|
};
|
|
35
|
+
/**
|
|
36
|
+
* Binds a vector, both as a persisted value and as the query vector of a distance expression, so a
|
|
37
|
+
* dialect needing a conversion around it (`$1::vector`, `VEC_FromText(?)`) declares it once.
|
|
38
|
+
*/
|
|
39
|
+
protected appendVectorValue(ctx: QueryContext, value: readonly unknown[], _field?: FieldOptions): void;
|
|
40
|
+
/**
|
|
41
|
+
* The vector type this dialect actually has for a declared one, so the cast follows the column
|
|
42
|
+
* rather than naming a type the engine does not define. See {@link MULTI_VECTOR_TYPE_DIALECTS}.
|
|
43
|
+
*/
|
|
44
|
+
protected supportedVectorType(cast: VectorCast): VectorCast;
|
|
38
45
|
/**
|
|
39
46
|
* Append a vector similarity function call: `fn(col, ?)`.
|
|
40
47
|
* Used by dialects that express vector distance via SQL functions (SQLite, MariaDB).
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { AbstractDialect } from './abstractDialect.js';
|
|
2
|
-
import {
|
|
2
|
+
import { MULTI_VECTOR_TYPE_DIALECTS } from './vectorCast.js';
|
|
3
3
|
/**
|
|
4
4
|
* Vector similarity search for SQL dialects: the `ORDER BY <distance>` expression, its projection as
|
|
5
5
|
* a named score, and the index metadata the schema generator reads.
|
|
@@ -13,8 +13,6 @@ import { resolveVectorCast } from './vectorCast.js';
|
|
|
13
13
|
* {@link appendVectorSort} or filling {@link vectorDistanceFns}.
|
|
14
14
|
*/
|
|
15
15
|
export class VectorSqlDialect extends AbstractDialect {
|
|
16
|
-
/** Vector index operator classes, keyed by distance metric. Partial: not every dialect supports every metric. */
|
|
17
|
-
vectorOpsClass = undefined;
|
|
18
16
|
vectorExtension = undefined;
|
|
19
17
|
/**
|
|
20
18
|
* Mapping of UQL vector distance metrics to native SQL functions.
|
|
@@ -30,21 +28,34 @@ export class VectorSqlDialect extends AbstractDialect {
|
|
|
30
28
|
const field = meta.fields[key];
|
|
31
29
|
const colName = this.resolveColumnName(key, field);
|
|
32
30
|
const distance = search.$distance ?? field?.distance ?? 'cosine';
|
|
33
|
-
|
|
34
|
-
|
|
31
|
+
return { colName, distance, field };
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Binds a vector, both as a persisted value and as the query vector of a distance expression, so a
|
|
35
|
+
* dialect needing a conversion around it (`$1::vector`, `VEC_FromText(?)`) declares it once.
|
|
36
|
+
*/
|
|
37
|
+
appendVectorValue(ctx, value, _field) {
|
|
38
|
+
ctx.addValue(`[${value.join(',')}]`);
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* The vector type this dialect actually has for a declared one, so the cast follows the column
|
|
42
|
+
* rather than naming a type the engine does not define. See {@link MULTI_VECTOR_TYPE_DIALECTS}.
|
|
43
|
+
*/
|
|
44
|
+
supportedVectorType(cast) {
|
|
45
|
+
return MULTI_VECTOR_TYPE_DIALECTS.has(this.dialectName) ? cast : 'vector';
|
|
35
46
|
}
|
|
36
47
|
/**
|
|
37
48
|
* Append a vector similarity function call: `fn(col, ?)`.
|
|
38
49
|
* Used by dialects that express vector distance via SQL functions (SQLite, MariaDB).
|
|
39
50
|
*/
|
|
40
51
|
appendFunctionVectorSort(ctx, meta, key, search) {
|
|
41
|
-
const { colName, distance } = this.resolveVectorSortParams(meta, key, search);
|
|
52
|
+
const { colName, distance, field } = this.resolveVectorSortParams(meta, key, search);
|
|
42
53
|
const fn = this.vectorDistanceFns.get(distance);
|
|
43
54
|
if (!fn) {
|
|
44
55
|
throw Error(`${this.dialectName} does not support vector distance metric: ${distance}`);
|
|
45
56
|
}
|
|
46
57
|
ctx.append(`${fn}(${this.escapeId(colName)}, `);
|
|
47
|
-
|
|
58
|
+
this.appendVectorValue(ctx, search.$vector, field);
|
|
48
59
|
ctx.append(')');
|
|
49
60
|
}
|
|
50
61
|
/**
|
|
@@ -64,6 +75,6 @@ export class VectorSqlDialect extends AbstractDialect {
|
|
|
64
75
|
this.appendFunctionVectorSort(ctx, meta, key, search);
|
|
65
76
|
return;
|
|
66
77
|
}
|
|
67
|
-
throw new TypeError(
|
|
78
|
+
throw new TypeError(`${this.dialectName} does not support vector similarity sort. Use raw() for vector queries.`);
|
|
68
79
|
}
|
|
69
80
|
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { FieldOptions, HookEvent, RelationOptions, Type } from '../../type/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* What the member decorators record for one class, waiting for `@Entity()` or `defineEntity` to drain
|
|
4
|
+
* it into the metadata registry. Member decorators receive no class reference under the standard
|
|
5
|
+
* decorator spec, so this object is the only channel between them and the class decorator that does.
|
|
6
|
+
*/
|
|
7
|
+
export type MemberRegistrations = {
|
|
8
|
+
readonly fields: Record<string, FieldOptions>;
|
|
9
|
+
readonly relations: Record<string, RelationOptions>;
|
|
10
|
+
readonly hooks: Partial<Record<HookEvent, string[]>>;
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* The calling class's own registrations, created on first use.
|
|
14
|
+
*
|
|
15
|
+
* Deliberately holds **only** this class's members: inheritance is resolved later by walking the class
|
|
16
|
+
* prototype chain, not by reading through the metadata object's. tsc chains a subclass's metadata to its
|
|
17
|
+
* parent's and SWC does not, so anything built on that chain would work under one compiler and quietly
|
|
18
|
+
* lose inherited fields under the other. Keeping each bag to its own members also means a parent's map
|
|
19
|
+
* is never shared with its subclasses, and hooks cannot be registered twice.
|
|
20
|
+
*/
|
|
21
|
+
export declare function memberRegistrations(metadata: DecoratorMetadata): MemberRegistrations;
|
|
22
|
+
/**
|
|
23
|
+
* Takes the registrations belonging to `metadata`, leaving none behind, so finalizing an entity twice
|
|
24
|
+
* cannot register its hooks twice.
|
|
25
|
+
*/
|
|
26
|
+
export declare function drainRegistrations(metadata: DecoratorMetadata | undefined): MemberRegistrations | undefined;
|
|
27
|
+
/**
|
|
28
|
+
* The registrations a class made for itself.
|
|
29
|
+
*
|
|
30
|
+
* @remarks Only usable once the class is fully defined, which is why `@Entity()` reads
|
|
31
|
+
* `context.metadata` instead: TypeScript attaches `Symbol.metadata` to the class *after* its class
|
|
32
|
+
* decorators return. Ancestors are always fully defined by then, so this is how inherited members are
|
|
33
|
+
* collected.
|
|
34
|
+
*/
|
|
35
|
+
export declare function ownRegistrations(entity: Type<unknown>): MemberRegistrations | undefined;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Polyfill `Symbol.metadata`, which no runtime we support defines yet (checked on Node 24 and Bun
|
|
3
|
+
* 1.3): TypeScript's decorator emit reads it to decide whether to build the metadata object at all, so
|
|
4
|
+
* without this every `context.metadata` is `undefined` and field registration is silently dropped
|
|
5
|
+
* rather than failing.
|
|
6
|
+
*
|
|
7
|
+
* `Symbol.for`, not `Symbol()`, so a duplicated copy of this module (HMR, federated bundles, ESM+CJS
|
|
8
|
+
* dual-loading) lands on the same symbol, and so it agrees with the key esbuild and SWC fall back to
|
|
9
|
+
* (`Symbol.metadata ?? Symbol.for('Symbol.metadata')`). Assigned through a widened alias because the
|
|
10
|
+
* lib declares the property `readonly`; when a runtime does define it, `??=` leaves it alone.
|
|
11
|
+
*/
|
|
12
|
+
const symbolCtor = Symbol;
|
|
13
|
+
symbolCtor.metadata ??= Symbol.for('Symbol.metadata');
|
|
14
|
+
/** Where member registrations live on the per-class metadata object. */
|
|
15
|
+
const registrations = Symbol.for('uql-orm/entity/decoratorMembers');
|
|
16
|
+
/**
|
|
17
|
+
* The calling class's own registrations, created on first use.
|
|
18
|
+
*
|
|
19
|
+
* Deliberately holds **only** this class's members: inheritance is resolved later by walking the class
|
|
20
|
+
* prototype chain, not by reading through the metadata object's. tsc chains a subclass's metadata to its
|
|
21
|
+
* parent's and SWC does not, so anything built on that chain would work under one compiler and quietly
|
|
22
|
+
* lose inherited fields under the other. Keeping each bag to its own members also means a parent's map
|
|
23
|
+
* is never shared with its subclasses, and hooks cannot be registered twice.
|
|
24
|
+
*/
|
|
25
|
+
export function memberRegistrations(metadata) {
|
|
26
|
+
if (!Object.hasOwn(metadata, registrations)) {
|
|
27
|
+
metadata[registrations] = { fields: {}, relations: {}, hooks: {} };
|
|
28
|
+
}
|
|
29
|
+
return metadata[registrations];
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Takes the registrations belonging to `metadata`, leaving none behind, so finalizing an entity twice
|
|
33
|
+
* cannot register its hooks twice.
|
|
34
|
+
*/
|
|
35
|
+
export function drainRegistrations(metadata) {
|
|
36
|
+
if (!metadata || !Object.hasOwn(metadata, registrations)) {
|
|
37
|
+
return undefined;
|
|
38
|
+
}
|
|
39
|
+
const own = metadata[registrations];
|
|
40
|
+
delete metadata[registrations];
|
|
41
|
+
return own;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* The registrations a class made for itself.
|
|
45
|
+
*
|
|
46
|
+
* @remarks Only usable once the class is fully defined, which is why `@Entity()` reads
|
|
47
|
+
* `context.metadata` instead: TypeScript attaches `Symbol.metadata` to the class *after* its class
|
|
48
|
+
* decorators return. Ancestors are always fully defined by then, so this is how inherited members are
|
|
49
|
+
* collected.
|
|
50
|
+
*/
|
|
51
|
+
export function ownRegistrations(entity) {
|
|
52
|
+
const metadata = Object.getOwnPropertyDescriptor(entity, Symbol.metadata)?.value;
|
|
53
|
+
return drainRegistrations(metadata);
|
|
54
|
+
}
|
|
@@ -1,2 +1,28 @@
|
|
|
1
|
-
import type { EntityOptions, Type } from '../../type/index.js';
|
|
2
|
-
|
|
1
|
+
import type { EntityOptions, FieldKey, FilterOptions, IndexColumnInput, IndexOptions, Type } from '../../type/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* Marks a class as an entity and finalizes its metadata.
|
|
4
|
+
*
|
|
5
|
+
* @remarks Takes the registrations from `context.metadata` rather than from the class. Member
|
|
6
|
+
* decorators have already run by the time a class decorator does, but TypeScript defines
|
|
7
|
+
* `Symbol.metadata` on the class *after* the class decorators return, so reading `entity[Symbol.metadata]`
|
|
8
|
+
* here would find only what the base class left behind. `defineEntity` reads it off the class instead,
|
|
9
|
+
* which is correct for the imperative path because it runs later still.
|
|
10
|
+
*/
|
|
11
|
+
export declare function Entity<E>(opts?: EntityOptions<E>): (entity: Type<E>, context?: ClassDecoratorContext) => void;
|
|
12
|
+
/**
|
|
13
|
+
* Registers a named `$where` filter, applied to every query unless bypassed via `QueryOptions.filters`.
|
|
14
|
+
*
|
|
15
|
+
* @example `@Filter('active', { condition: { status: 'active' }, default: false })`
|
|
16
|
+
*/
|
|
17
|
+
export declare function Filter<E>(name: string, opts: FilterOptions<E>): (entity: Type<E>) => void;
|
|
18
|
+
/**
|
|
19
|
+
* Declares a composite index. Stacks, so several may sit above one class.
|
|
20
|
+
*
|
|
21
|
+
* `E` is inferred from the class the returned decorator is applied to, which is what lets the column
|
|
22
|
+
* names be checked against it: `@Index(['nope'])` does not compile.
|
|
23
|
+
*
|
|
24
|
+
* @example `@Index(['lastName', 'firstName'], { name: 'idx_users_fullname' })`
|
|
25
|
+
* @example `@Index(['email'], { unique: true })`
|
|
26
|
+
* @example `@Index(['status'], { where: "status = 'active'" })`
|
|
27
|
+
*/
|
|
28
|
+
export declare function Index<E>(columns: readonly IndexColumnInput<FieldKey<E>>[], options?: IndexOptions): (entity: Type<E>) => void;
|
|
@@ -1,6 +1,44 @@
|
|
|
1
|
-
import { defineEntity } from '../metadata/definition.js';
|
|
1
|
+
import { applyMembers, defineEntity, defineFilter, defineIndex } from '../metadata/definition.js';
|
|
2
|
+
import { drainRegistrations } from './bag.js';
|
|
3
|
+
// The class-level decorators. Unlike the member ones they receive the class, so each is a direct call
|
|
4
|
+
// into the registry with no bag in between.
|
|
5
|
+
/**
|
|
6
|
+
* Marks a class as an entity and finalizes its metadata.
|
|
7
|
+
*
|
|
8
|
+
* @remarks Takes the registrations from `context.metadata` rather than from the class. Member
|
|
9
|
+
* decorators have already run by the time a class decorator does, but TypeScript defines
|
|
10
|
+
* `Symbol.metadata` on the class *after* the class decorators return, so reading `entity[Symbol.metadata]`
|
|
11
|
+
* here would find only what the base class left behind. `defineEntity` reads it off the class instead,
|
|
12
|
+
* which is correct for the imperative path because it runs later still.
|
|
13
|
+
*/
|
|
2
14
|
export function Entity(opts) {
|
|
3
|
-
return (entity) => {
|
|
15
|
+
return (entity, context) => {
|
|
16
|
+
applyMembers(entity, drainRegistrations(context?.metadata));
|
|
4
17
|
defineEntity(entity, opts);
|
|
5
18
|
};
|
|
6
19
|
}
|
|
20
|
+
/**
|
|
21
|
+
* Registers a named `$where` filter, applied to every query unless bypassed via `QueryOptions.filters`.
|
|
22
|
+
*
|
|
23
|
+
* @example `@Filter('active', { condition: { status: 'active' }, default: false })`
|
|
24
|
+
*/
|
|
25
|
+
export function Filter(name, opts) {
|
|
26
|
+
return (entity) => {
|
|
27
|
+
defineFilter(entity, name, opts);
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Declares a composite index. Stacks, so several may sit above one class.
|
|
32
|
+
*
|
|
33
|
+
* `E` is inferred from the class the returned decorator is applied to, which is what lets the column
|
|
34
|
+
* names be checked against it: `@Index(['nope'])` does not compile.
|
|
35
|
+
*
|
|
36
|
+
* @example `@Index(['lastName', 'firstName'], { name: 'idx_users_fullname' })`
|
|
37
|
+
* @example `@Index(['email'], { unique: true })`
|
|
38
|
+
* @example `@Index(['status'], { where: "status = 'active'" })`
|
|
39
|
+
*/
|
|
40
|
+
export function Index(columns, options = {}) {
|
|
41
|
+
return (entity) => {
|
|
42
|
+
defineIndex(entity, { ...options, columns });
|
|
43
|
+
};
|
|
44
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { EntityGetter, FieldOptions, FieldType, RelationManyToManyOptions, RelationManyToOneOptions, RelationOneToManyOptions, RelationOneToOneOptions, TsTypeOf } from '../../type/index.js';
|
|
2
|
+
/** A member decorator that also constrains the property it may be applied to. */
|
|
3
|
+
type MemberDecorator<V> = (value: undefined, context: ClassFieldDecoratorContext<unknown, V>) => void;
|
|
4
|
+
/**
|
|
5
|
+
* Declares a persisted field.
|
|
6
|
+
*
|
|
7
|
+
* `@Field({ type: String })` on a `number` property is a compile error rather than a silent TEXT column,
|
|
8
|
+
* which is what makes the now-mandatory `type` worth stating.
|
|
9
|
+
*
|
|
10
|
+
* @example `@Field({ type: String }) name?: string;`
|
|
11
|
+
* @example `@Field({ references: () => Company }) companyId?: string;`
|
|
12
|
+
*/
|
|
13
|
+
export declare function Field<T extends FieldType>(opts: FieldOptions & {
|
|
14
|
+
readonly type: T;
|
|
15
|
+
}): MemberDecorator<TsTypeOf<T> | undefined>;
|
|
16
|
+
/**
|
|
17
|
+
* A foreign key may omit `type`: schema generation resolves it from the referenced primary key, so the
|
|
18
|
+
* column picks up that key's `columnType`, length and chained references rather than a guess.
|
|
19
|
+
*/
|
|
20
|
+
export declare function Field(opts: FieldOptions & {
|
|
21
|
+
readonly references: EntityGetter;
|
|
22
|
+
}): MemberDecorator<unknown>;
|
|
23
|
+
/**
|
|
24
|
+
* Declares the primary key, checked the same way as `@Field`.
|
|
25
|
+
*
|
|
26
|
+
* @example `@Id({ type: Number }) id?: number;`
|
|
27
|
+
* @example `@Id({ type: 'uuid', onInsert: uuidv7 }) id?: string;`
|
|
28
|
+
*/
|
|
29
|
+
export declare function Id<T extends FieldType>(opts: FieldOptions & {
|
|
30
|
+
readonly type: T;
|
|
31
|
+
}): MemberDecorator<TsTypeOf<T> | undefined>;
|
|
32
|
+
/**
|
|
33
|
+
* `E` comes from the mandatory `entity` getter, so the context can insist the property really holds that
|
|
34
|
+
* entity: `@ManyToOne({ entity: () => Other })` on a `Company` field stops compiling, and a to-many
|
|
35
|
+
* cardinality on a non-array property does too. `entity` is required because nothing reflects it now.
|
|
36
|
+
*/
|
|
37
|
+
type WithEntity<E, O> = O & {
|
|
38
|
+
readonly entity: EntityGetter<E>;
|
|
39
|
+
};
|
|
40
|
+
export declare function OneToOne<E>(opts: WithEntity<E, RelationOneToOneOptions<E>>): MemberDecorator<E | undefined>;
|
|
41
|
+
export declare function ManyToOne<E>(opts: WithEntity<E, RelationManyToOneOptions<E>>): MemberDecorator<E | undefined>;
|
|
42
|
+
export declare function OneToMany<E>(opts: WithEntity<E, RelationOneToManyOptions<E>>): MemberDecorator<readonly E[] | undefined>;
|
|
43
|
+
export declare function ManyToMany<E>(opts: WithEntity<E, RelationManyToManyOptions<E>>): MemberDecorator<readonly E[] | undefined>;
|
|
44
|
+
export declare const BeforeInsert: () => <This>(_value: unknown, context: ClassMethodDecoratorContext<This>) => void;
|
|
45
|
+
export declare const AfterInsert: () => <This>(_value: unknown, context: ClassMethodDecoratorContext<This>) => void;
|
|
46
|
+
export declare const BeforeUpdate: () => <This>(_value: unknown, context: ClassMethodDecoratorContext<This>) => void;
|
|
47
|
+
export declare const AfterUpdate: () => <This>(_value: unknown, context: ClassMethodDecoratorContext<This>) => void;
|
|
48
|
+
export declare const BeforeDelete: () => <This>(_value: unknown, context: ClassMethodDecoratorContext<This>) => void;
|
|
49
|
+
export declare const AfterDelete: () => <This>(_value: unknown, context: ClassMethodDecoratorContext<This>) => void;
|
|
50
|
+
export declare const AfterLoad: () => <This>(_value: unknown, context: ClassMethodDecoratorContext<This>) => void;
|
|
51
|
+
export {};
|