@uql/core 3.13.0 → 3.14.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/CHANGELOG.md +45 -2
- package/README.md +23 -2
- package/dist/browser/type/clientQuerier.d.ts +11 -2
- package/dist/browser/type/clientQuerier.d.ts.map +1 -1
- package/dist/dialect/abstractSqlDialect.d.ts +13 -5
- package/dist/dialect/abstractSqlDialect.d.ts.map +1 -1
- package/dist/dialect/abstractSqlDialect.js +70 -90
- package/dist/dialect/abstractSqlDialect.js.map +1 -1
- package/dist/express/querierMiddleware.d.ts.map +1 -1
- package/dist/express/querierMiddleware.js +9 -24
- package/dist/express/querierMiddleware.js.map +1 -1
- package/dist/maria/mariadbQuerier.d.ts +1 -5
- package/dist/maria/mariadbQuerier.d.ts.map +1 -1
- package/dist/maria/mariadbQuerier.js +2 -2
- package/dist/maria/mariadbQuerier.js.map +1 -1
- package/dist/migrate/cli.d.ts +4 -4
- package/dist/migrate/cli.d.ts.map +1 -1
- package/dist/migrate/cli.js.map +1 -1
- package/dist/migrate/generator/mongoSchemaGenerator.d.ts +2 -2
- package/dist/migrate/generator/mongoSchemaGenerator.d.ts.map +1 -1
- package/dist/migrate/generator/mongoSchemaGenerator.js.map +1 -1
- package/dist/migrate/introspection/abstractSqlSchemaIntrospector.d.ts +7 -7
- package/dist/migrate/introspection/abstractSqlSchemaIntrospector.d.ts.map +1 -1
- package/dist/migrate/introspection/abstractSqlSchemaIntrospector.js.map +1 -1
- package/dist/migrate/introspection/mongoIntrospector.js +1 -1
- package/dist/migrate/introspection/mongoIntrospector.js.map +1 -1
- package/dist/migrate/introspection/postgresIntrospector.d.ts +2 -2
- package/dist/migrate/introspection/postgresIntrospector.d.ts.map +1 -1
- package/dist/migrate/introspection/postgresIntrospector.js +0 -1
- package/dist/migrate/introspection/postgresIntrospector.js.map +1 -1
- package/dist/migrate/storage/databaseStorage.js.map +1 -1
- package/dist/mongo/mongoDialect.d.ts +2 -2
- package/dist/mongo/mongoDialect.d.ts.map +1 -1
- package/dist/mongo/mongoDialect.js +2 -8
- package/dist/mongo/mongoDialect.js.map +1 -1
- package/dist/mysql/mysql2Querier.d.ts +1 -1
- package/dist/mysql/mysql2Querier.d.ts.map +1 -1
- package/dist/mysql/mysql2Querier.js +6 -5
- package/dist/mysql/mysql2Querier.js.map +1 -1
- package/dist/neon/neonQuerier.d.ts +1 -5
- package/dist/neon/neonQuerier.d.ts.map +1 -1
- package/dist/neon/neonQuerier.js +2 -3
- package/dist/neon/neonQuerier.js.map +1 -1
- package/dist/postgres/pgQuerier.d.ts +3 -7
- package/dist/postgres/pgQuerier.d.ts.map +1 -1
- package/dist/postgres/pgQuerier.js +3 -3
- package/dist/postgres/pgQuerier.js.map +1 -1
- package/dist/querier/abstractQuerier.d.ts +2 -2
- package/dist/querier/abstractQuerier.d.ts.map +1 -1
- package/dist/querier/abstractQuerier.js +2 -7
- package/dist/querier/abstractQuerier.js.map +1 -1
- package/dist/querier/abstractSqlQuerier.d.ts.map +1 -1
- package/dist/querier/abstractSqlQuerier.js +1 -1
- package/dist/querier/abstractSqlQuerier.js.map +1 -1
- package/dist/type/entity.d.ts +1 -2
- package/dist/type/entity.d.ts.map +1 -1
- package/dist/type/query.d.ts +20 -34
- package/dist/type/query.d.ts.map +1 -1
- package/dist/type/query.js.map +1 -1
- package/dist/type/universalQuerier.d.ts +16 -16
- package/dist/type/universalQuerier.d.ts.map +1 -1
- package/dist/type/utility.d.ts +8 -1
- package/dist/type/utility.d.ts.map +1 -1
- package/dist/util/dialect.util.d.ts +1 -1
- package/dist/util/dialect.util.d.ts.map +1 -1
- package/dist/util/dialect.util.js +3 -6
- package/dist/util/dialect.util.js.map +1 -1
- package/dist/util/sql.util.d.ts +9 -1
- package/dist/util/sql.util.d.ts.map +1 -1
- package/dist/util/sql.util.js +10 -0
- package/dist/util/sql.util.js.map +1 -1
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -3,12 +3,12 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
-
# [3.
|
|
6
|
+
# [3.14.0](https://github.com/rogerpadilla/uql/compare/@uql/core@3.13.1...@uql/core@3.14.0) (2026-03-07)
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
### Features
|
|
10
10
|
|
|
11
|
-
*
|
|
11
|
+
* Standardize the `$select` query parameter to accept an object instead of an array for field selection. ([b4dca74](https://github.com/rogerpadilla/uql/commit/b4dca74e65013c14dd508c9ccea646b44eee1f3e))
|
|
12
12
|
|
|
13
13
|
|
|
14
14
|
|
|
@@ -20,6 +20,49 @@ All notable changes to this project will be documented in this file. Please add
|
|
|
20
20
|
|
|
21
21
|
date format is [yyyy-mm-dd]
|
|
22
22
|
|
|
23
|
+
## [3.14.0] - 2026-03-07
|
|
24
|
+
### Type Safety
|
|
25
|
+
- **Map-Only `$select`**: `$select` now only accepts the map form (e.g., `{ id: true, name: true }`), removing the less type-safe array form. Relation selections are now additive in map form.
|
|
26
|
+
- **Stricter `$and`/`$or`/`$not`/`$nor`**: Logical operators now only accept `QueryWhereMap | QueryRaw` elements — bare ID values (e.g., `$or: [5]`) must use the explicit form `$or: [{ id: 5 }]`. This restores TypeScript's excess property checking inside logical clauses.
|
|
27
|
+
- **Wider JSON Array Operators**: `$elemMatch` and `$all` now accept JSON fields without requiring `as any` casts, thanks to widened fallback types for non-array field types. Removed 6 unnecessary `as any` casts from tests.
|
|
28
|
+
|
|
29
|
+
### Refactoring
|
|
30
|
+
- **Simplified Express Middleware**: `$where` ID injection in `querierMiddleware` now always uses map form, properly converting array `$where` from query strings to `{ id: { $in: [...] } }`.
|
|
31
|
+
- **Removed `QueryWhereSingle`**: Consolidated into a flattened `QueryWhere<E>` union. Introduced reusable `QueryWhereArray<E>` type alias.
|
|
32
|
+
|
|
33
|
+
## [3.13.1] - 2026-03-07
|
|
34
|
+
### Type Safety
|
|
35
|
+
- **Fully Typed Querier Returns**: Remaining querier methods now return proper types instead of `unknown`, enabling IDE autocompletion and compile-time validation on query results for all methods.
|
|
36
|
+
- **Semantic `RawRow` Type**: Introduced `RawRow` as a reusable semantic alias for raw database result rows, replacing scattered `Record<string, unknown>` and `any` across queriers, introspection, and SQL utilities.
|
|
37
|
+
- **Typed MySQL Driver**: Replaced `any` in MySQL2 querier with proper `ResultSetHeader` type from the driver.
|
|
38
|
+
- **Smarter `$select` Validation**: Field and relation selections are now validated simultaneously, catching invalid property names at compile time.
|
|
39
|
+
- **Stricter Null Comparisons**: `null` is now only accepted in `$eq` and `$ne` operators — invalid comparisons like `$gt: null` are caught at compile time.
|
|
40
|
+
- **Typed `defaultValue`**: Entity field defaults are now type-checked instead of accepting `any`.
|
|
41
|
+
|
|
42
|
+
### API Surface & DX
|
|
43
|
+
- **Cleaner Querier Interfaces**: `ClientQuerier` and `UniversalQuerier` are now properly separated with documented contracts, preventing confusing type mismatches.
|
|
44
|
+
- **Reduced Public API**: Removed unused/redundant type exports (`QuerySearchOne`, `QueryConflictPathsMap`), making the API surface smaller and easier to navigate.
|
|
45
|
+
- **Improved JSDoc**: Added cross-references between related operators (`$not` root vs field) for better discoverability.
|
|
46
|
+
|
|
47
|
+
### Refactoring
|
|
48
|
+
- **DRY Relation Iteration**: Consolidated duplicated relation iteration logic into `forEachJoinableRelation`, eliminating ~35 duplicated lines.
|
|
49
|
+
- **DRY `compareJsonPath`**: Simplified from 6 parameters to 3, removing redundant internal calls.
|
|
50
|
+
- **DRY `extractInsertResult`**: Shared utility for INSERT result ID extraction across all RETURNING-based drivers (pg, neon, maria), eliminating duplicated logic.
|
|
51
|
+
- **Eliminated Type Casts**: Replaced `Record<string, unknown>` casts with proper type guards across the dialect layer.
|
|
52
|
+
- **Typo Fix**: Renamed `buldQueryWhereAsMap` → `buildQueryWhereAsMap`.
|
|
53
|
+
|
|
54
|
+
## [3.13.0] - 2026-03-07
|
|
55
|
+
### New Features
|
|
56
|
+
- **`QueryRaw` Class Refactoring**: Replaced the opaque type + type-guard pattern with a proper `class` using `Symbol`-keyed properties (`RAW_VALUE`, `RAW_ALIAS`). Enables `instanceof QueryRaw` checks, eliminates autocomplete pollution, and prevents accidental structural matches.
|
|
57
|
+
- **JSON `$merge`/`$unset` Operators**: Restored type-safe partial update of JSONB fields via `$merge` (shallow merge) and `$unset` (key removal) in `update()` payloads. Works across PostgreSQL (`||`/`-`), MySQL (`JSON_MERGE_PATCH`/`JSON_REMOVE`), and SQLite (`json_patch`/`json_remove`).
|
|
58
|
+
```ts
|
|
59
|
+
await querier.updateMany(Company, { $where: { id: 1 } }, {
|
|
60
|
+
kind: { $merge: { theme: 'dark' }, $unset: ['deprecated'] },
|
|
61
|
+
});
|
|
62
|
+
```
|
|
63
|
+
- **JSON Dot-Notation Sorting**: `$sort` now supports JSONB dot-notation paths (e.g. `{ 'kind.priority': 'desc' }`), sharing the `resolveJsonDotPath` helper with `$where` for DRY consistency.
|
|
64
|
+
|
|
65
|
+
|
|
23
66
|
## [3.12.1] - 2026-03-05
|
|
24
67
|
### Bug Fixes
|
|
25
68
|
- **Null-Safe JSONB `$ne`**: JSONB dot-notation `$ne` now uses null-safe operators (`IS DISTINCT FROM` on PostgreSQL, `IS NOT` on SQLite) so that absent keys (which return SQL `NULL`) are correctly included in results. Previously, `{ 'settings.isArchived': { $ne: true } }` would silently exclude rows where the key didn't exist.
|
package/README.md
CHANGED
|
@@ -28,7 +28,7 @@ const users = await querier.findMany(User, {
|
|
|
28
28
|
| **[Naming Strategies](https://uql.app/naming-strategy)** | Pluggable system to translate between TypeScript `camelCase` and database `snake_case`. |
|
|
29
29
|
| **Smart SQL Engine** | Optimized sub-queries, placeholders ($1, $2), and minimal SQL generation via `QueryContext`. |
|
|
30
30
|
| **Thread-Safe by Design** | Centralized task queue and `@Serialized()` decorator prevent race conditions. |
|
|
31
|
-
| **[Declarative Transactions](https://
|
|
31
|
+
| **[Declarative Transactions](https://uql.app/transactions)** | Standard `@Transactional()` and `@InjectQuerier()` decorators for NestJS/DI. |
|
|
32
32
|
| **[Modern & Versatile](https://uql.app/entities/virtual-fields)** | **Pure ESM**, high-res timing, [Soft-delete](https://uql.app/entities/soft-delete), and **Vector/JSONB/JSON** support. |
|
|
33
33
|
| **[Database Migrations](https://www.uql.app/migrations)** | Built-in [Entity-First synchronization](https://uql.app/migrations#3-entity-first-synchronization-development) and a robust CLI for version-controlled schema evolution. |
|
|
34
34
|
| **[Logging & Monitoring](https://www.uql.app/logging)** | Professional-grade monitoring with slow-query detection and colored output. |
|
|
@@ -266,7 +266,7 @@ try {
|
|
|
266
266
|
const users = await querier.findMany(User, {
|
|
267
267
|
$select: {
|
|
268
268
|
name: true,
|
|
269
|
-
profile: { $select:
|
|
269
|
+
profile: { $select: { bio: true }, $required: true } // INNER JOIN
|
|
270
270
|
},
|
|
271
271
|
$where: {
|
|
272
272
|
status: 'active',
|
|
@@ -375,6 +375,27 @@ export class UserService {
|
|
|
375
375
|
}
|
|
376
376
|
```
|
|
377
377
|
|
|
378
|
+
#### Option C: Imperative
|
|
379
|
+
|
|
380
|
+
For granular control over the transaction lifecycle, manage `begin`, `commit`, `rollback`, and `release` yourself.
|
|
381
|
+
|
|
382
|
+
```ts
|
|
383
|
+
const querier = await pool.getQuerier();
|
|
384
|
+
try {
|
|
385
|
+
await querier.beginTransaction();
|
|
386
|
+
|
|
387
|
+
const userId = await querier.insertOne(User, { name: '...' });
|
|
388
|
+
await querier.insertOne(Profile, { userId, picture: '...' });
|
|
389
|
+
|
|
390
|
+
await querier.commitTransaction();
|
|
391
|
+
} catch (error) {
|
|
392
|
+
await querier.rollbackTransaction();
|
|
393
|
+
throw error;
|
|
394
|
+
} finally {
|
|
395
|
+
await querier.release();
|
|
396
|
+
}
|
|
397
|
+
```
|
|
398
|
+
|
|
378
399
|
|
|
379
400
|
|
|
380
401
|
## 5. Migrations & Synchronization
|
|
@@ -1,6 +1,15 @@
|
|
|
1
|
-
import type { IdValue, Query, QueryOne, QueryOptions, QuerySearch, Type,
|
|
1
|
+
import type { IdValue, Query, QueryOne, QueryOptions, QuerySearch, Type, UpdatePayload } from '../../type/index.js';
|
|
2
2
|
import type { RequestOptions, RequestSuccessResponse } from './request.js';
|
|
3
|
-
|
|
3
|
+
/**
|
|
4
|
+
* Client-side querier — mirrors {@link UniversalQuerier} method names and semantics but with two structural differences:
|
|
5
|
+
* 1. Every return type is wrapped in `RequestSuccessResponse<T>` (adds `data`/`count` envelope).
|
|
6
|
+
* 2. Every method accepts an extra `opts?: RequestOptions` parameter.
|
|
7
|
+
*
|
|
8
|
+
* These differences prevent clean `extends UniversalQuerier` — TypeScript does not support
|
|
9
|
+
* higher-kinded type wrappers, so the interfaces are kept in sync by convention.
|
|
10
|
+
* @see UniversalQuerier for the server-side contract with direct return types.
|
|
11
|
+
*/
|
|
12
|
+
export interface ClientQuerier {
|
|
4
13
|
findOneById<E>(entity: Type<E>, id: IdValue<E>, q?: QueryOne<E>, opts?: RequestOptions): Promise<RequestSuccessResponse<E | undefined>>;
|
|
5
14
|
findOne<E>(entity: Type<E>, q: QueryOne<E>, opts?: RequestOptions): Promise<RequestSuccessResponse<E | undefined>>;
|
|
6
15
|
findMany<E>(entity: Type<E>, q: Query<E>, opts?: RequestOptions): Promise<RequestSuccessResponse<E[]>>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"clientQuerier.d.ts","sourceRoot":"","sources":["../../../src/browser/type/clientQuerier.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"clientQuerier.d.ts","sourceRoot":"","sources":["../../../src/browser/type/clientQuerier.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,YAAY,EAAE,WAAW,EAAE,IAAI,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpH,OAAO,KAAK,EAAE,cAAc,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAC;AAE3E;;;;;;;;GAQG;AACH,MAAM,WAAW,aAAa;IAC5B,WAAW,CAAC,CAAC,EACX,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC,EACf,EAAE,EAAE,OAAO,CAAC,CAAC,CAAC,EACd,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC,CAAC,EACf,IAAI,CAAC,EAAE,cAAc,GACpB,OAAO,CAAC,sBAAsB,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC,CAAC;IAElD,OAAO,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,QAAQ,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,sBAAsB,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC,CAAC;IAEnH,QAAQ,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,sBAAsB,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAEvG,gBAAgB,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,sBAAsB,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAE/G,KAAK,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,WAAW,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,sBAAsB,CAAC,MAAM,CAAC,CAAC,CAAC;IAE9G,SAAS,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,sBAAsB,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAE9G,aAAa,CAAC,CAAC,EACb,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC,EACf,EAAE,EAAE,OAAO,CAAC,CAAC,CAAC,EACd,OAAO,EAAE,aAAa,CAAC,CAAC,CAAC,EACzB,IAAI,CAAC,EAAE,cAAc,GACpB,OAAO,CAAC,sBAAsB,CAAC,MAAM,CAAC,CAAC,CAAC;IAE3C,OAAO,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,sBAAsB,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAE5G,aAAa,CAAC,CAAC,EACb,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC,EACf,EAAE,EAAE,OAAO,CAAC,CAAC,CAAC,EACd,IAAI,CAAC,EAAE,YAAY,GAAG,cAAc,GACnC,OAAO,CAAC,sBAAsB,CAAC,MAAM,CAAC,CAAC,CAAC;IAE3C,UAAU,CAAC,CAAC,EACV,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC,EACf,EAAE,EAAE,WAAW,CAAC,CAAC,CAAC,EAClB,IAAI,CAAC,EAAE,YAAY,GAAG,cAAc,GACnC,OAAO,CAAC,sBAAsB,CAAC,MAAM,CAAC,CAAC,CAAC;CAC5C"}
|
|
@@ -12,14 +12,19 @@ export declare abstract class AbstractSqlDialect extends AbstractDialect impleme
|
|
|
12
12
|
placeholder(_index: number): string;
|
|
13
13
|
returningId<E>(entity: Type<E>): string;
|
|
14
14
|
search<E>(ctx: QueryContext, entity: Type<E>, q?: Query<E>, opts?: QueryOptions): void;
|
|
15
|
-
selectFields<E>(ctx: QueryContext, entity: Type<E>, select: QuerySelect<E> | undefined, opts?: QuerySelectOptions): void;
|
|
16
|
-
select<E>(ctx: QueryContext, entity: Type<E>, select: QuerySelect<E> | undefined, opts?: QueryOptions): void;
|
|
15
|
+
selectFields<E>(ctx: QueryContext, entity: Type<E>, select: QuerySelect<E> | QueryRaw[] | undefined, opts?: QuerySelectOptions): void;
|
|
16
|
+
select<E>(ctx: QueryContext, entity: Type<E>, select: QuerySelect<E> | QueryRaw[] | undefined, opts?: QueryOptions): void;
|
|
17
17
|
protected selectRelationFields<E>(ctx: QueryContext, entity: Type<E>, select: QuerySelect<E> | undefined, opts?: {
|
|
18
18
|
prefix?: string;
|
|
19
19
|
}): void;
|
|
20
20
|
protected selectRelationJoins<E>(ctx: QueryContext, entity: Type<E>, select: QuerySelect<E> | undefined, opts?: {
|
|
21
21
|
prefix?: string;
|
|
22
22
|
}): void;
|
|
23
|
+
/**
|
|
24
|
+
* Iterates over joinable (11/m1) relations for a given select, resolving shared metadata.
|
|
25
|
+
* Used by both `selectRelationFields` and `selectRelationJoins` to avoid duplicated iteration logic.
|
|
26
|
+
*/
|
|
27
|
+
private forEachJoinableRelation;
|
|
23
28
|
where<E>(ctx: QueryContext, entity: Type<E>, where?: QueryWhere<E>, opts?: QueryWhereOptions): void;
|
|
24
29
|
compare<E>(ctx: QueryContext, entity: Type<E>, key: string, val: unknown, opts?: QueryComparisonOptions): void;
|
|
25
30
|
protected compareLogicalOperator<E>(ctx: QueryContext, entity: Type<E>, key: '$and' | '$or' | '$not' | '$nor', val: QueryWhereArray<E>, opts: QueryComparisonOptions): void;
|
|
@@ -69,16 +74,19 @@ export declare abstract class AbstractSqlDialect extends AbstractDialect impleme
|
|
|
69
74
|
*
|
|
70
75
|
* @returns resolved metadata or `undefined` if the key is not a JSON dot-notation path
|
|
71
76
|
*/
|
|
72
|
-
protected resolveJsonDotPath<E>(meta: EntityMeta<E>, key: string): {
|
|
77
|
+
protected resolveJsonDotPath<E>(meta: EntityMeta<E>, key: string, prefix?: string): {
|
|
73
78
|
root: string;
|
|
74
79
|
jsonPath: string;
|
|
75
80
|
config: JsonFieldConfig;
|
|
76
81
|
} | undefined;
|
|
77
82
|
/**
|
|
78
83
|
* Compare a JSONB dot-notation path, e.g. `'settings.isArchived': { $ne: true }`.
|
|
79
|
-
*
|
|
84
|
+
* Receives a pre-resolved `resolveJsonDotPath` result to avoid redundant computation.
|
|
80
85
|
*/
|
|
81
|
-
protected compareJsonPath
|
|
86
|
+
protected compareJsonPath(ctx: QueryContext, resolved: {
|
|
87
|
+
jsonPath: string;
|
|
88
|
+
config: JsonFieldConfig;
|
|
89
|
+
}, val: unknown): void;
|
|
82
90
|
/**
|
|
83
91
|
* Returns a dialect-specific `JsonFieldConfig` for accessing a nested JSON path.
|
|
84
92
|
* Dialects should override this to provide their specific JSON accessor syntax.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"abstractSqlDialect.d.ts","sourceRoot":"","sources":["../../src/dialect/abstractSqlDialect.ts"],"names":[],"mappings":"AACA,OAAO,EACL,KAAK,UAAU,EACf,KAAK,QAAQ,EACb,KAAK,YAAY,EAEjB,KAAK,WAAW,EAEhB,KAAK,KAAK,EACV,KAAK,sBAAsB,EAC3B,KAAK,kBAAkB,EACvB,KAAK,YAAY,EACjB,KAAK,YAAY,EACjB,KAAK,YAAY,EACjB,KAAK,UAAU,EACf,QAAQ,EACR,KAAK,iBAAiB,EACtB,KAAK,WAAW,EAChB,KAAK,WAAW,
|
|
1
|
+
{"version":3,"file":"abstractSqlDialect.d.ts","sourceRoot":"","sources":["../../src/dialect/abstractSqlDialect.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { getMeta } from '../entity/index.js';
|
|
2
2
|
import { QueryRaw, RAW_ALIAS, RAW_VALUE, } from '../type/index.js';
|
|
3
|
-
import {
|
|
3
|
+
import { buildQueryWhereAsMap, buildSortMap, escapeSqlId, fillOnFields, filterFieldKeys, filterRelationKeys, flatObject, getFieldCallbackValue, getFieldKeys, getKeys, hasKeys, isJsonType, isSelectingRelations, raw, } from '../util/index.js';
|
|
4
4
|
import { AbstractDialect } from './abstractDialect.js';
|
|
5
5
|
import { SqlQueryContext } from './queryContext.js';
|
|
6
6
|
export class AbstractSqlDialect extends AbstractDialect {
|
|
@@ -48,15 +48,18 @@ export class AbstractSqlDialect extends AbstractDialect {
|
|
|
48
48
|
let selectArr;
|
|
49
49
|
if (select) {
|
|
50
50
|
if (Array.isArray(select)) {
|
|
51
|
+
// Internal-only path: raw SQL expressions passed as QueryRaw[]
|
|
51
52
|
selectArr = select;
|
|
52
53
|
}
|
|
53
54
|
else {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
55
|
+
// Only field keys affect whitelist/exclusion mode; relation keys are additive
|
|
56
|
+
const fieldEntries = getKeys(select).filter((it) => it in meta.fields);
|
|
57
|
+
const positiveFields = fieldEntries.filter((it) => select[it]);
|
|
58
|
+
const negativeFields = fieldEntries.filter((it) => !select[it]);
|
|
59
|
+
selectArr = positiveFields.length
|
|
60
|
+
? positiveFields
|
|
61
|
+
: getFieldKeys(meta.fields).filter((it) => !negativeFields.includes(it));
|
|
58
62
|
}
|
|
59
|
-
selectArr = selectArr.filter((it) => it instanceof QueryRaw || it in meta.fields);
|
|
60
63
|
const id = meta.id;
|
|
61
64
|
if (id && opts.prefix && !selectArr.includes(id)) {
|
|
62
65
|
selectArr = [id, ...selectArr];
|
|
@@ -108,86 +111,30 @@ export class AbstractSqlDialect extends AbstractDialect {
|
|
|
108
111
|
select(ctx, entity, select, opts = {}) {
|
|
109
112
|
const meta = getMeta(entity);
|
|
110
113
|
const tableName = this.resolveTableName(entity, meta);
|
|
111
|
-
const
|
|
114
|
+
const mapSelect = Array.isArray(select) ? undefined : select;
|
|
115
|
+
const prefix = (opts.prefix ?? (opts.autoPrefix || isSelectingRelations(meta, mapSelect))) ? tableName : undefined;
|
|
112
116
|
ctx.append('SELECT ');
|
|
113
117
|
this.selectFields(ctx, entity, select, { prefix });
|
|
114
118
|
// Add related fields BEFORE FROM clause
|
|
115
|
-
this.selectRelationFields(ctx, entity,
|
|
119
|
+
this.selectRelationFields(ctx, entity, mapSelect, { prefix });
|
|
116
120
|
ctx.append(` FROM ${this.escapeId(tableName)}`);
|
|
117
121
|
// Add JOINs AFTER FROM clause
|
|
118
|
-
this.selectRelationJoins(ctx, entity,
|
|
122
|
+
this.selectRelationJoins(ctx, entity, mapSelect, { prefix });
|
|
119
123
|
}
|
|
120
124
|
selectRelationFields(ctx, entity, select, opts = {}) {
|
|
121
|
-
|
|
122
|
-
return;
|
|
123
|
-
}
|
|
124
|
-
const meta = getMeta(entity);
|
|
125
|
-
const tableName = this.resolveTableName(entity, meta);
|
|
126
|
-
const relKeys = filterRelationKeys(meta, select);
|
|
127
|
-
const isSelectArray = Array.isArray(select);
|
|
128
|
-
const prefix = opts.prefix;
|
|
129
|
-
for (const relKey of relKeys) {
|
|
130
|
-
const relOpts = meta.relations[relKey];
|
|
131
|
-
if (!relOpts)
|
|
132
|
-
continue;
|
|
133
|
-
if (relOpts.cardinality === '1m' || relOpts.cardinality === 'mm') {
|
|
134
|
-
continue;
|
|
135
|
-
}
|
|
136
|
-
const isFirstLevel = prefix === tableName;
|
|
137
|
-
const joinRelAlias = isFirstLevel ? relKey : prefix ? prefix + '.' + relKey : relKey;
|
|
138
|
-
if (!relOpts.entity)
|
|
139
|
-
continue;
|
|
140
|
-
const relEntity = relOpts.entity();
|
|
141
|
-
const relSelect = select[relKey];
|
|
142
|
-
const relQuery = isSelectArray
|
|
143
|
-
? {}
|
|
144
|
-
: Array.isArray(relSelect)
|
|
145
|
-
? { $select: relSelect }
|
|
146
|
-
: (relSelect ?? {});
|
|
125
|
+
this.forEachJoinableRelation(entity, select, opts, (relEntity, relQuery, joinRelAlias) => {
|
|
147
126
|
ctx.append(', ');
|
|
148
|
-
this.selectFields(ctx, relEntity, relQuery.$select, {
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
});
|
|
152
|
-
// Recursively add nested relation fields
|
|
153
|
-
this.selectRelationFields(ctx, relEntity, relQuery.$select, {
|
|
154
|
-
prefix: joinRelAlias,
|
|
155
|
-
});
|
|
156
|
-
}
|
|
127
|
+
this.selectFields(ctx, relEntity, relQuery.$select, { prefix: joinRelAlias, autoPrefixAlias: true });
|
|
128
|
+
this.selectRelationFields(ctx, relEntity, relQuery.$select, { prefix: joinRelAlias });
|
|
129
|
+
});
|
|
157
130
|
}
|
|
158
131
|
selectRelationJoins(ctx, entity, select, opts = {}) {
|
|
159
|
-
|
|
160
|
-
return;
|
|
161
|
-
}
|
|
162
|
-
const meta = getMeta(entity);
|
|
163
|
-
const tableName = this.resolveTableName(entity, meta);
|
|
164
|
-
const relKeys = filterRelationKeys(meta, select);
|
|
165
|
-
const isSelectArray = Array.isArray(select);
|
|
166
|
-
const prefix = opts.prefix;
|
|
167
|
-
for (const relKey of relKeys) {
|
|
168
|
-
const relOpts = meta.relations[relKey];
|
|
169
|
-
if (!relOpts)
|
|
170
|
-
continue;
|
|
171
|
-
if (relOpts.cardinality === '1m' || relOpts.cardinality === 'mm') {
|
|
172
|
-
continue;
|
|
173
|
-
}
|
|
174
|
-
const isFirstLevel = prefix === tableName;
|
|
175
|
-
const joinRelAlias = isFirstLevel ? relKey : prefix ? prefix + '.' + relKey : relKey;
|
|
176
|
-
if (!relOpts.entity)
|
|
177
|
-
continue;
|
|
178
|
-
const relEntity = relOpts.entity();
|
|
179
|
-
const relSelect = select[relKey];
|
|
180
|
-
const relQuery = isSelectArray
|
|
181
|
-
? {}
|
|
182
|
-
: Array.isArray(relSelect)
|
|
183
|
-
? { $select: relSelect }
|
|
184
|
-
: (relSelect ?? {});
|
|
132
|
+
this.forEachJoinableRelation(entity, select, opts, (relEntity, relQuery, joinRelAlias, relOpts, meta, tableName, required) => {
|
|
185
133
|
const relMeta = getMeta(relEntity);
|
|
186
134
|
const relTableName = this.resolveTableName(relEntity, relMeta);
|
|
187
135
|
const relEntityName = this.escapeId(relTableName);
|
|
188
|
-
const relPath = prefix ? this.escapeId(prefix, true) : this.escapeId(tableName);
|
|
189
|
-
const
|
|
190
|
-
const joinType = relQuery[required] ? 'INNER' : 'LEFT';
|
|
136
|
+
const relPath = opts.prefix ? this.escapeId(opts.prefix, true) : this.escapeId(tableName);
|
|
137
|
+
const joinType = required ? 'INNER' : 'LEFT';
|
|
191
138
|
const joinAlias = this.escapeId(joinRelAlias, true);
|
|
192
139
|
ctx.append(` ${joinType} JOIN ${relEntityName} ${joinAlias} ON `);
|
|
193
140
|
ctx.append((relOpts.references ?? [])
|
|
@@ -203,16 +150,47 @@ export class AbstractSqlDialect extends AbstractDialect {
|
|
|
203
150
|
ctx.append(' AND ');
|
|
204
151
|
this.where(ctx, relEntity, relQuery.$where, { prefix: joinRelAlias, clause: false });
|
|
205
152
|
}
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
153
|
+
this.selectRelationJoins(ctx, relEntity, relQuery.$select, { prefix: joinRelAlias });
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Iterates over joinable (11/m1) relations for a given select, resolving shared metadata.
|
|
158
|
+
* Used by both `selectRelationFields` and `selectRelationJoins` to avoid duplicated iteration logic.
|
|
159
|
+
*/
|
|
160
|
+
forEachJoinableRelation(entity, select, opts, callback) {
|
|
161
|
+
if (!select)
|
|
162
|
+
return;
|
|
163
|
+
const meta = getMeta(entity);
|
|
164
|
+
const tableName = this.resolveTableName(entity, meta);
|
|
165
|
+
const relKeys = filterRelationKeys(meta, select);
|
|
166
|
+
const prefix = opts.prefix;
|
|
167
|
+
for (const relKey of relKeys) {
|
|
168
|
+
const relOpts = meta.relations[relKey];
|
|
169
|
+
if (!relOpts || relOpts.cardinality === '1m' || relOpts.cardinality === 'mm' || !relOpts.entity)
|
|
170
|
+
continue;
|
|
171
|
+
const isFirstLevel = prefix === tableName;
|
|
172
|
+
const joinRelAlias = isFirstLevel ? relKey : prefix ? `${prefix}.${relKey}` : relKey;
|
|
173
|
+
const relEntity = relOpts.entity();
|
|
174
|
+
const relSelect = select?.[relKey];
|
|
175
|
+
let relQuery;
|
|
176
|
+
let required = false;
|
|
177
|
+
if (isRelationSelectQuery(relSelect)) {
|
|
178
|
+
relQuery = relSelect;
|
|
179
|
+
required = relSelect.$required === true;
|
|
180
|
+
}
|
|
181
|
+
else if (Array.isArray(relSelect)) {
|
|
182
|
+
relQuery = { $select: relSelect };
|
|
183
|
+
}
|
|
184
|
+
else {
|
|
185
|
+
relQuery = {};
|
|
186
|
+
}
|
|
187
|
+
callback(relEntity, relQuery, joinRelAlias, relOpts, meta, tableName, required);
|
|
210
188
|
}
|
|
211
189
|
}
|
|
212
190
|
where(ctx, entity, where = {}, opts = {}) {
|
|
213
191
|
const meta = getMeta(entity);
|
|
214
192
|
const { usePrecedence, clause = 'WHERE', softDelete } = opts;
|
|
215
|
-
where =
|
|
193
|
+
where = buildQueryWhereAsMap(meta, where);
|
|
216
194
|
if (meta.softDelete &&
|
|
217
195
|
(softDelete || softDelete === undefined) &&
|
|
218
196
|
!where[meta.softDelete]) {
|
|
@@ -283,9 +261,9 @@ export class AbstractSqlDialect extends AbstractDialect {
|
|
|
283
261
|
}
|
|
284
262
|
// Detect JSONB dot-notation: 'column.path' where column is a registered JSON/JSONB field
|
|
285
263
|
const keyStr = key;
|
|
286
|
-
const jsonDot = this.resolveJsonDotPath(meta, keyStr);
|
|
264
|
+
const jsonDot = this.resolveJsonDotPath(meta, keyStr, opts.prefix);
|
|
287
265
|
if (jsonDot) {
|
|
288
|
-
this.compareJsonPath(ctx,
|
|
266
|
+
this.compareJsonPath(ctx, jsonDot, val);
|
|
289
267
|
return;
|
|
290
268
|
}
|
|
291
269
|
// Detect relation filtering: key is a known relation with 'mm' or '1m' cardinality
|
|
@@ -603,7 +581,7 @@ export class AbstractSqlDialect extends AbstractDialect {
|
|
|
603
581
|
count(ctx, entity, q, opts) {
|
|
604
582
|
const search = { ...q };
|
|
605
583
|
delete search.$sort;
|
|
606
|
-
this.select(ctx, entity, [raw('COUNT(*)', 'count')]
|
|
584
|
+
this.select(ctx, entity, [raw('COUNT(*)', 'count')]);
|
|
607
585
|
this.search(ctx, entity, search, opts);
|
|
608
586
|
}
|
|
609
587
|
find(ctx, entity, q = {}, opts) {
|
|
@@ -825,7 +803,7 @@ export class AbstractSqlDialect extends AbstractDialect {
|
|
|
825
803
|
*
|
|
826
804
|
* @returns resolved metadata or `undefined` if the key is not a JSON dot-notation path
|
|
827
805
|
*/
|
|
828
|
-
resolveJsonDotPath(meta, key) {
|
|
806
|
+
resolveJsonDotPath(meta, key, prefix) {
|
|
829
807
|
const dotIndex = key.indexOf('.');
|
|
830
808
|
if (dotIndex <= 0) {
|
|
831
809
|
return undefined;
|
|
@@ -837,20 +815,16 @@ export class AbstractSqlDialect extends AbstractDialect {
|
|
|
837
815
|
}
|
|
838
816
|
const jsonPath = key.slice(dotIndex + 1);
|
|
839
817
|
const colName = this.resolveColumnName(root, field);
|
|
840
|
-
const escapedCol = this.escapeId(colName);
|
|
818
|
+
const escapedCol = (prefix ? this.escapeId(prefix, true, true) : '') + this.escapeId(colName);
|
|
841
819
|
const config = this.getJsonFieldConfig(escapedCol, jsonPath);
|
|
842
820
|
return { root, jsonPath, config };
|
|
843
821
|
}
|
|
844
822
|
/**
|
|
845
823
|
* Compare a JSONB dot-notation path, e.g. `'settings.isArchived': { $ne: true }`.
|
|
846
|
-
*
|
|
824
|
+
* Receives a pre-resolved `resolveJsonDotPath` result to avoid redundant computation.
|
|
847
825
|
*/
|
|
848
|
-
compareJsonPath(ctx,
|
|
849
|
-
const
|
|
850
|
-
const field = meta.fields[root];
|
|
851
|
-
const columnName = this.resolveColumnName(root, field);
|
|
852
|
-
const escapedColumn = (opts.prefix ? this.escapeId(opts.prefix, true, true) : '') + this.escapeId(columnName);
|
|
853
|
-
const config = this.getJsonFieldConfig(escapedColumn, jsonPath);
|
|
826
|
+
compareJsonPath(ctx, resolved, val) {
|
|
827
|
+
const { jsonPath, config } = resolved;
|
|
854
828
|
const value = this.normalizeWhereValue(val);
|
|
855
829
|
const operators = getKeys(value);
|
|
856
830
|
if (operators.length > 1) {
|
|
@@ -964,4 +938,10 @@ export class AbstractSqlDialect extends AbstractDialect {
|
|
|
964
938
|
ctx.append(')');
|
|
965
939
|
}
|
|
966
940
|
}
|
|
941
|
+
/**
|
|
942
|
+
* Type guard: narrows a relation select value to a query object (with optional `$required`).
|
|
943
|
+
*/
|
|
944
|
+
function isRelationSelectQuery(val) {
|
|
945
|
+
return val !== null && typeof val === 'object' && !Array.isArray(val);
|
|
946
|
+
}
|
|
967
947
|
//# sourceMappingURL=abstractSqlDialect.js.map
|