typeorm-extensions 2.2.0-rc.1 → 2.2.0-rc.11
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 +70 -39
- package/dist/decorators/calculated-column.decorator.d.ts +7 -3
- package/dist/decorators/calculated-column.decorator.js +35 -6
- package/dist/decorators/generated-column.decorator.d.ts +17 -0
- package/dist/decorators/generated-column.decorator.js +15 -0
- package/dist/decorators/index.d.ts +1 -0
- package/dist/decorators/index.js +1 -0
- package/dist/extensions/calculated-column.extension.d.ts +3 -3
- package/dist/extensions/calculated-column.extension.js +3 -16
- package/dist/extensions/cursor-pagination.extension.d.ts +15 -2
- package/dist/extensions/cursor-pagination.extension.js +90 -13
- package/dist/extensions/get-many-with-totals.extension.js +12 -31
- package/dist/extensions/join-lateral.extension.js +4 -6
- package/dist/extensions/order.extension.d.ts +2 -2
- package/dist/extensions/pagination.extension.d.ts +7 -1
- package/dist/extensions/pagination.extension.js +5 -12
- package/dist/extensions/period-filter.extension.d.ts +4 -0
- package/dist/extensions/period-filter.extension.js +1 -9
- package/dist/extensions/typed/index.d.ts +2 -0
- package/dist/extensions/typed/index.js +2 -0
- package/dist/extensions/typed/typed-calculated/typed-calculated-column.extension.d.ts +19 -0
- package/dist/extensions/typed/typed-calculated/typed-calculated-column.extension.js +17 -0
- package/dist/extensions/typed/typed-distinct/typed-distinct.extension.d.ts +6 -4
- package/dist/extensions/typed/typed-distinct/typed-distinct.extension.js +9 -2
- package/dist/extensions/typed/typed-group-by/typed-group-by.extension.d.ts +8 -7
- package/dist/extensions/typed/typed-group-by/typed-group-by.extension.js +1 -1
- package/dist/extensions/typed/typed-having/typed-having.extension.d.ts +4 -4
- package/dist/extensions/typed/typed-having/typed-having.extension.js +1 -1
- package/dist/extensions/typed/typed-joins/typed-join-and-map.extension.d.ts +19 -0
- package/dist/extensions/typed/typed-joins/typed-join-and-map.extension.js +23 -0
- package/dist/extensions/typed/typed-joins/typed-joins.extension.d.ts +4 -4
- package/dist/extensions/typed/typed-joins/typed-joins.extension.js +11 -22
- package/dist/extensions/typed/typed-order-by/typed-order-by-array-position.extension.d.ts +2 -2
- package/dist/extensions/typed/typed-order-by/typed-order-by.extension.d.ts +4 -2
- package/dist/extensions/typed/typed-order-by/typed-order-by.extension.js +18 -17
- package/dist/extensions/typed/typed-returning/typed-returning.extension.js +1 -9
- package/dist/extensions/typed/typed-root.extension.d.ts +26 -4
- package/dist/extensions/typed/typed-select/typed-select.extension.d.ts +6 -6
- package/dist/extensions/typed/typed-select/typed-select.extension.js +3 -9
- package/dist/extensions/typed/typed-where/typed-period-filter.extension.d.ts +5 -1
- package/dist/extensions/typed/typed-where/typed-period-filter.extension.js +1 -9
- package/dist/extensions/typed/typed-where/typed-where-array.extension.d.ts +13 -9
- package/dist/extensions/typed/typed-where/typed-where-array.extension.js +4 -16
- package/dist/extensions/typed/typed-where/typed-where-ilike.extension.d.ts +10 -6
- package/dist/extensions/typed/typed-where/typed-where-ilike.extension.js +1 -9
- package/dist/extensions/typed/typed-where/typed-where-is-any.extension.d.ts +10 -6
- package/dist/extensions/typed/typed-where/typed-where-is-any.extension.js +3 -20
- package/dist/extensions/typed/typed-where/typed-where-is-distinct-from.extension.d.ts +10 -6
- package/dist/extensions/typed/typed-where/typed-where-is-distinct-from.extension.js +3 -20
- package/dist/extensions/typed/typed-where/typed-where-is-in.extension.d.ts +10 -6
- package/dist/extensions/typed/typed-where/typed-where-is-in.extension.js +3 -21
- package/dist/extensions/typed/typed-where/typed-where-json.extension.d.ts +10 -6
- package/dist/extensions/typed/typed-where/typed-where-json.extension.js +4 -21
- package/dist/extensions/typed/typed-where/typed-where-lower.extension.d.ts +10 -6
- package/dist/extensions/typed/typed-where/typed-where-lower.extension.js +1 -9
- package/dist/extensions/typed/typed-where/typed-where-similar.extension.d.ts +8 -4
- package/dist/extensions/typed/typed-where/typed-where-similar.extension.js +1 -8
- package/dist/extensions/typed/typed-where/typed-where.extension.d.ts +7 -3
- package/dist/extensions/typed/typed-where/typed-where.extension.js +2 -10
- package/dist/extensions/union.extension.js +1 -1
- package/dist/extensions/where-array.extension.d.ts +4 -0
- package/dist/extensions/where-array.extension.js +1 -9
- package/dist/extensions/where-exists.extension.js +4 -15
- package/dist/extensions/where-is-any.extension.d.ts +41 -128
- package/dist/extensions/where-is-any.extension.js +1 -9
- package/dist/extensions/where-is-distinct-from.extension.d.ts +4 -0
- package/dist/extensions/where-is-distinct-from.extension.js +1 -9
- package/dist/extensions/where-is-in.extension.d.ts +38 -116
- package/dist/extensions/where-is-in.extension.js +11 -27
- package/dist/extensions/where-json.extension.d.ts +4 -0
- package/dist/extensions/where-json.extension.js +1 -9
- package/dist/extensions/where-relation-exists.extension.d.ts +6 -6
- package/dist/extensions/where-relation-exists.extension.js +5 -25
- package/dist/helpers.d.ts +5 -0
- package/dist/helpers.js +12 -3
- package/dist/index.d.ts +1 -2
- package/dist/index.js +1 -4
- package/dist/types/extensions/cursor-pagination.types.d.ts +16 -1
- package/dist/types/extensions/get-many-with-totals.types.d.ts +4 -0
- package/dist/types/extensions/index.d.ts +1 -0
- package/dist/types/extensions/index.js +1 -0
- package/dist/types/extensions/typed-group-by.types.d.ts +2 -2
- package/dist/types/extensions/typed-join-and-map.types.d.ts +6 -0
- package/dist/types/extensions/typed-join-and-map.types.js +2 -0
- package/dist/types/interfaces/cursor-pagination.interface.d.ts +11 -0
- package/dist/types/modules/typed-root.types.d.ts +11 -9
- package/dist/utils/calculated-column.utils.d.ts +11 -0
- package/dist/utils/calculated-column.utils.js +33 -4
- package/dist/utils/common.utils.d.ts +2 -0
- package/dist/utils/common.utils.js +4 -1
- package/dist/utils/cursor.utils.d.ts +5 -0
- package/dist/utils/cursor.utils.js +18 -7
- package/dist/utils/json-path.utils.js +2 -2
- package/dist/utils/pagination.utils.d.ts +10 -0
- package/dist/utils/pagination.utils.js +27 -10
- package/dist/utils/parameter.utils.d.ts +10 -0
- package/dist/utils/parameter.utils.js +22 -1
- package/dist/utils/period.utils.js +2 -1
- package/dist/utils/prototype.utils.d.ts +9 -0
- package/dist/utils/prototype.utils.js +12 -2
- package/dist/utils/query-builder.utils.d.ts +43 -4
- package/dist/utils/query-builder.utils.js +239 -36
- package/dist/utils/reserved-qualifier-guard.utils.d.ts +36 -0
- package/dist/utils/reserved-qualifier-guard.utils.js +98 -0
- package/dist/utils/reserved-qualifier.utils.d.ts +33 -0
- package/dist/utils/reserved-qualifier.utils.js +252 -0
- package/dist/utils/table.utils.d.ts +27 -11
- package/dist/utils/table.utils.js +58 -23
- package/dist/utils/time-zone.utils.d.ts +8 -0
- package/dist/utils/time-zone.utils.js +15 -5
- package/dist/utils/typed-where.utils.d.ts +9 -0
- package/dist/utils/typed-where.utils.js +17 -0
- package/dist/utils/where.utils.d.ts +2 -0
- package/dist/utils/where.utils.js +21 -14
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -5,62 +5,93 @@
|
|
|
5
5
|
[](https://github.com/koenigstag/typeorm-extensions/issues)
|
|
6
6
|
[](https://github.com/koenigstag/typeorm-extensions/pulls)
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
**Type-safe extensions for the TypeORM QueryBuilder.** Write selects, joins, conditions and ordering as property callbacks the compiler checks against your entities, page through results with totals or cursors, and build the raw SQL you still need with helpers that quote it for the database.
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
```typescript
|
|
11
|
+
import 'typeorm-extensions';
|
|
11
12
|
|
|
12
|
-
|
|
13
|
+
const { list, total, hasMore } = await dataSource
|
|
14
|
+
.getRepository(UserEntity)
|
|
15
|
+
.createQueryBuilder('user')
|
|
16
|
+
.leftJoinTyped(user => user.images, 'images')
|
|
17
|
+
.whereTyped(user => user.createdAt, '>', since)
|
|
18
|
+
// a key inside a JSON column: "user"."metadata" ->> 'role' = :param
|
|
19
|
+
.andWhereTyped(user => user.metadata.role, '=', 'admin')
|
|
20
|
+
.orderByTyped(user => user.images.url, 'ASC', 'NULLS LAST')
|
|
21
|
+
.getManyWithTotals({ page: 1, pageSize: 20 });
|
|
22
|
+
```
|
|
13
23
|
|
|
14
|
-
|
|
15
|
-
- Type Safety: the library is written in TypeScript and provides type-safe QueryBuilder methods based on Entity metadata.
|
|
16
|
-
- Flexibility: the library is designed to be flexible and can be used with any TypeORM entity.
|
|
24
|
+
Try it in the [playground](https://koenigstag.github.io/typeorm-extensions/playground/): an editor and a database in the browser.
|
|
17
25
|
|
|
18
|
-
##
|
|
26
|
+
## Why
|
|
19
27
|
|
|
20
|
-
|
|
28
|
+
The QueryBuilder takes columns as strings. A renamed property, a typo or a half-quoted alias compiles and then fails at runtime:
|
|
21
29
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
30
|
+
```typescript
|
|
31
|
+
.where('user.createdAt > :since', { since }) // still compiles after createdAt is renamed
|
|
32
|
+
.andWhere('order."userId" = :userId', { userId }) // PostgreSQL: syntax error at or near "order"
|
|
25
33
|
```
|
|
26
34
|
|
|
27
|
-
|
|
35
|
+
With the extensions a column is a property of the entity type, so the compiler catches the rename, and the SQL is built and quoted for you:
|
|
28
36
|
|
|
29
37
|
```typescript
|
|
30
|
-
|
|
38
|
+
.whereTyped(user => user.createdAt, '>', since)
|
|
31
39
|
|
|
32
|
-
|
|
40
|
+
const orders = tableHelper(OrderEntity, 'order');
|
|
41
|
+
.andWhere(`${orders.userId} = :userId`, { userId }) // "order"."userId" = :userId
|
|
42
|
+
```
|
|
33
43
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
.leftJoinTyped(user => user.images, 'images')
|
|
39
|
-
// Select type-safe properties
|
|
40
|
-
.selectTyped(user => [
|
|
41
|
-
user.id,
|
|
42
|
-
user.name,
|
|
43
|
-
user.email,
|
|
44
|
-
user.images.url,
|
|
45
|
-
])
|
|
46
|
-
.whereTyped(user => user.name, 'ILIKE', 'John')
|
|
47
|
-
// Paths into JSON columns: "users"."metadata" ->> 'role' = :param
|
|
48
|
-
.andWhereTyped(user => user.metadata.role, '=', 'admin')
|
|
49
|
-
// Order by type-safe own and relation properties
|
|
50
|
-
.orderByTyped(user => user.images.url, 'ASC', 'NULLS LAST')
|
|
51
|
-
// Use pagination as simple as that
|
|
52
|
-
.applyPaginationFilter({ page: 1, pageSize: 10 }, { useTakeAndSkip: true });
|
|
44
|
+
## Installation
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
npm install typeorm-extensions
|
|
53
48
|
```
|
|
54
49
|
|
|
55
|
-
|
|
50
|
+
`typeorm` 0.3.12 or later, or 1.x, is a peer dependency. Release candidates of the next version are published under the `next` tag: `npm install typeorm-extensions@next`.
|
|
51
|
+
|
|
52
|
+
## Features
|
|
53
|
+
|
|
54
|
+
**Typed queries** ([docs](https://koenigstag.github.io/typeorm-extensions/docs/api/typed/))
|
|
55
|
+
- `selectTyped`, `whereTyped` (keys inside JSON columns too; `strictParams` checks the parameter against the column type), `orderByTyped`, `groupByTyped` / `havingTyped`, `distinctOnTyped`, `returningTyped`
|
|
56
|
+
- typed joins and map joins (`leftJoinTyped`, `innerJoinAndMapOneTyped`, …), `typed<Root>()` for the aliases a query joins, `whereRelationExists` built from the relation metadata
|
|
57
|
+
|
|
58
|
+
**Pagination** ([docs](https://koenigstag.github.io/typeorm-extensions/docs/api/pagination/))
|
|
59
|
+
- `getManyWithTotals` and `getRawManyWithTotals`: the rows of a page, the total, `page` / `pageSize`, `hasMore` and the order it was read in
|
|
60
|
+
- `getManyWithCursor` and `getRawManyWithCursor`: keyset pagination with an opaque cursor, by columns or by SQL expressions
|
|
61
|
+
- `applyPagination` for a page of any query
|
|
56
62
|
|
|
57
|
-
|
|
63
|
+
**Filters and ordering** ([docs](https://koenigstag.github.io/typeorm-extensions/docs/api/where/))
|
|
64
|
+
- `whereIsIn` / `whereIsNotIn` that handle empty arrays, `whereIsAny`, null-safe `whereIsDistinctFrom`
|
|
65
|
+
- case-insensitive search with escaped wildcards, `applyPeriodFilter` in a time zone
|
|
66
|
+
- `applyOrder` from a client's order filter, `orderByArrayPosition`
|
|
58
67
|
|
|
59
|
-
|
|
68
|
+
**Subqueries and joins**
|
|
69
|
+
- `whereExists` / `whereNotExists`, with the subquery's parameters renamed so they never clash with the outer query's
|
|
70
|
+
- `leftJoinLateral` / `innerJoinLateral`, `innerJoinUnnest`, `fromUnion` and CTE unions
|
|
71
|
+
|
|
72
|
+
**SQL helpers** ([docs](https://koenigstag.github.io/typeorm-extensions/docs/api/utils/tableHelper/))
|
|
73
|
+
- `tableHelper`: quoted column references, conditions, counts, sums, JSON objects and paths, time zones. Given the entity class it writes the database name of a renamed column
|
|
74
|
+
- the reserved qualifier guard: catches SQL like `order."userId"` before PostgreSQL rejects it
|
|
75
|
+
|
|
76
|
+
**Columns** ([docs](https://koenigstag.github.io/typeorm-extensions/docs/api/columns/generatedColumn/))
|
|
77
|
+
- `@CalculatedColumn` with `addSelectCalculated` / `orderByCalculated`: a value each query computes
|
|
78
|
+
- `@GeneratedColumn`: a column the database generates, which TypeORM reads and never writes
|
|
79
|
+
|
|
80
|
+
## Entry points
|
|
81
|
+
|
|
82
|
+
| Import | Gives |
|
|
83
|
+
|---|---|
|
|
84
|
+
| `import 'typeorm-extensions'` | every QueryBuilder extension, and all exports |
|
|
85
|
+
| `import 'typeorm-extensions/dist/extensions/pagination.extension'` | one extension |
|
|
86
|
+
| `import { tableHelper, GeneratedColumn } from 'typeorm-extensions/helpers'` | the helpers, with no side effects |
|
|
87
|
+
|
|
88
|
+
## Compatibility
|
|
89
|
+
|
|
90
|
+
TypeORM 0.3.12 and later, and 1.x. CI runs the test suite on SQLite with TypeORM 0.3.12, the latest 0.3 and 1.x, and on MySQL 8.4. Features that only work on some databases (array and JSON operators, `DISTINCT ON`, `orderByArrayPosition`, …) say so in their docs.
|
|
91
|
+
|
|
92
|
+
## Documentation
|
|
60
93
|
|
|
61
|
-
|
|
62
|
-
- [x] Calculated columns: the `@CalculatedColumn` decorator and `addSelectCalculated`. `getMany`, `getOne` and `getManyWithTotals` fill them as they are, so they need no variants of their own
|
|
63
|
-
- [x] Order by a calculated column: `orderByCalculated`, and `applyOrder` orders a `@CalculatedColumn` field by its value
|
|
94
|
+
The [documentation](https://koenigstag.github.io/typeorm-extensions/) has a page for every method, with its signature, options and examples, and the [playground](https://koenigstag.github.io/typeorm-extensions/playground/) runs them.
|
|
64
95
|
|
|
65
96
|
## License
|
|
66
97
|
|
|
@@ -3,11 +3,15 @@ export type CalculatedColumnOptions = {
|
|
|
3
3
|
/**
|
|
4
4
|
* The type the selected value is converted from, as for `@Column`: `'boolean'`, `'jsonb'`, `'timestamptz'`.
|
|
5
5
|
* Defaults to the property's TypeScript type with `emitDecoratorMetadata`: `Number`, `String`, `Boolean`, `Date`.
|
|
6
|
-
* `Number`
|
|
7
|
-
*
|
|
6
|
+
* `Number` and the numeric SQL types (`'integer'`, `'bigint'`, `'numeric'`, `'decimal'`, `'float'`, …) turn
|
|
7
|
+
* numeric strings (a PostgreSQL `COUNT`, an `AVG`) into numbers, decimals kept; `Boolean` reads `0` / `1`,
|
|
8
|
+
* `'0'` / `'1'` and `t` / `f` as well.
|
|
8
9
|
*/
|
|
9
10
|
type?: ColumnType;
|
|
10
|
-
/**
|
|
11
|
+
/**
|
|
12
|
+
* Converts the selected value, e.g. JSON text from SQLite. Only `from` is used. It replaces the conversion of
|
|
13
|
+
* the type: it gets the value as the database returned it, a PostgreSQL `COUNT` as a string.
|
|
14
|
+
*/
|
|
11
15
|
transformer?: ValueTransformer | ValueTransformer[];
|
|
12
16
|
};
|
|
13
17
|
/**
|
|
@@ -21,7 +21,7 @@ const typeorm_1 = require("typeorm");
|
|
|
21
21
|
*/
|
|
22
22
|
function CalculatedColumn(typeOrOptions) {
|
|
23
23
|
return (object, propertyName) => {
|
|
24
|
-
var _a
|
|
24
|
+
var _a;
|
|
25
25
|
const options = typeof typeOrOptions === 'object' ? typeOrOptions : { type: typeOrOptions };
|
|
26
26
|
const reflectedType = typeof Reflect === 'object' && typeof Reflect.getMetadata === 'function'
|
|
27
27
|
? Reflect.getMetadata('design:type', object, propertyName)
|
|
@@ -32,8 +32,9 @@ function CalculatedColumn(typeOrOptions) {
|
|
|
32
32
|
throw new TypeError(`@CalculatedColumn() on ${object.constructor.name}.${String(propertyName)} needs a type: ` +
|
|
33
33
|
`@CalculatedColumn('varchar'), or a property type of number, string, boolean or Date`);
|
|
34
34
|
}
|
|
35
|
-
const
|
|
36
|
-
|
|
35
|
+
const ownConversion = ownConversions.get(typeof type === 'string' ? type.toLowerCase() : type);
|
|
36
|
+
// a transformer of its own replaces the conversion and takes the value as the database returned it
|
|
37
|
+
const conversion = options.transformer === undefined ? ownConversion : undefined;
|
|
37
38
|
// the mode of TypeORM's @VirtualColumn: not a column of the table, never inserted or updated;
|
|
38
39
|
// with no `query` and `select: false` TypeORM never selects it by itself
|
|
39
40
|
(0, typeorm_1.getMetadataArgsStorage)().columns.push({
|
|
@@ -42,12 +43,12 @@ function CalculatedColumn(typeOrOptions) {
|
|
|
42
43
|
mode: 'virtual-property',
|
|
43
44
|
options: {
|
|
44
45
|
// the drivers parseInt a Number (AVG 150.5 → 150) and read MySQL's '0' as true, so these
|
|
45
|
-
// types are converted here; String is a type no driver converts
|
|
46
|
-
type:
|
|
46
|
+
// types are converted here; String is a type no driver converts
|
|
47
|
+
type: ownConversion ? String : type,
|
|
47
48
|
select: false,
|
|
48
49
|
insert: false,
|
|
49
50
|
update: false,
|
|
50
|
-
transformer: conversion
|
|
51
|
+
transformer: conversion !== null && conversion !== void 0 ? conversion : options.transformer,
|
|
51
52
|
},
|
|
52
53
|
});
|
|
53
54
|
};
|
|
@@ -74,8 +75,36 @@ function toBoolean(value) {
|
|
|
74
75
|
}
|
|
75
76
|
const numberConversion = { from: toNumber, to: (value) => value };
|
|
76
77
|
const booleanConversion = { from: toBoolean, to: (value) => value };
|
|
78
|
+
// a COUNT is a bigint and an AVG a numeric, which drivers hand over as strings to keep their precision
|
|
79
|
+
const numericTypes = [
|
|
80
|
+
'int',
|
|
81
|
+
'int2',
|
|
82
|
+
'int4',
|
|
83
|
+
'int8',
|
|
84
|
+
'int64',
|
|
85
|
+
'integer',
|
|
86
|
+
'tinyint',
|
|
87
|
+
'smallint',
|
|
88
|
+
'mediumint',
|
|
89
|
+
'bigint',
|
|
90
|
+
'unsigned big int',
|
|
91
|
+
'long',
|
|
92
|
+
'numeric',
|
|
93
|
+
'decimal',
|
|
94
|
+
'dec',
|
|
95
|
+
'fixed',
|
|
96
|
+
'smalldecimal',
|
|
97
|
+
'number',
|
|
98
|
+
'float',
|
|
99
|
+
'float4',
|
|
100
|
+
'float8',
|
|
101
|
+
'double',
|
|
102
|
+
'double precision',
|
|
103
|
+
'real',
|
|
104
|
+
];
|
|
77
105
|
const ownConversions = new Map([
|
|
78
106
|
[Number, numberConversion],
|
|
107
|
+
...numericTypes.map((type) => [type, numberConversion]),
|
|
79
108
|
[Boolean, booleanConversion],
|
|
80
109
|
['boolean', booleanConversion],
|
|
81
110
|
['bool', booleanConversion],
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { ColumnOptions, ColumnType } from 'typeorm';
|
|
2
|
+
/** The options of `@Column`, without those that would let TypeORM write the column. */
|
|
3
|
+
export type GeneratedColumnOptions = Omit<ColumnOptions, 'insert' | 'update' | 'readonly'>;
|
|
4
|
+
/**
|
|
5
|
+
* A column of the table that the database computes: `GENERATED ALWAYS AS (…) STORED`. TypeORM reads it like any
|
|
6
|
+
* column and never writes it. `asExpression` alone doesn't stop TypeORM (0.3 and 1.x) from sending the column in
|
|
7
|
+
* `save`, `insert` and `update`, which the database rejects for a generated column.
|
|
8
|
+
*
|
|
9
|
+
* Pass `asExpression` and `generatedType: 'STORED'` when TypeORM creates the schema (synchronize, generated
|
|
10
|
+
* migrations); a column created by a hand-written migration needs neither.
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* \@GeneratedColumn({ type: 'integer', asExpression: 'price * quantity', generatedType: 'STORED' })
|
|
14
|
+
* total: number;
|
|
15
|
+
*/
|
|
16
|
+
export declare function GeneratedColumn(options?: GeneratedColumnOptions): PropertyDecorator;
|
|
17
|
+
export declare function GeneratedColumn(type: ColumnType, options?: GeneratedColumnOptions): PropertyDecorator;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.GeneratedColumn = void 0;
|
|
4
|
+
const typeorm_1 = require("typeorm");
|
|
5
|
+
function GeneratedColumn(typeOrOptions, options = {}) {
|
|
6
|
+
const columnOptions = typeof typeOrOptions === 'object'
|
|
7
|
+
? { ...typeOrOptions }
|
|
8
|
+
: typeOrOptions === undefined
|
|
9
|
+
? { ...options }
|
|
10
|
+
: { ...options, type: typeOrOptions };
|
|
11
|
+
// TypeORM 0.3 applies `readonly` after `update`: `readonly: false` from an untyped caller would turn writes back on
|
|
12
|
+
delete columnOptions.readonly;
|
|
13
|
+
return (0, typeorm_1.Column)({ ...columnOptions, insert: false, update: false });
|
|
14
|
+
}
|
|
15
|
+
exports.GeneratedColumn = GeneratedColumn;
|
package/dist/decorators/index.js
CHANGED
|
@@ -15,3 +15,4 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
17
|
__exportStar(require("./calculated-column.decorator"), exports);
|
|
18
|
+
__exportStar(require("./generated-column.decorator"), exports);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { ObjectLiteral
|
|
2
|
-
|
|
3
|
-
export type CalculatedSelection
|
|
1
|
+
import { ObjectLiteral } from 'typeorm';
|
|
2
|
+
import { CalculatedSelection } from '../utils/calculated-column.utils';
|
|
3
|
+
export type { CalculatedSelection };
|
|
4
4
|
declare module 'typeorm/query-builder/SelectQueryBuilder' {
|
|
5
5
|
interface SelectQueryBuilder<Entity extends ObjectLiteral> {
|
|
6
6
|
/**
|
|
@@ -7,27 +7,14 @@ const calculated_column_utils_1 = require("../utils/calculated-column.utils");
|
|
|
7
7
|
const extension = {
|
|
8
8
|
prototype: {
|
|
9
9
|
addSelectCalculated(selection, property, alias) {
|
|
10
|
-
return
|
|
10
|
+
return (0, calculated_column_utils_1.addSelectCalculatedColumn)(this, selection, property, alias, 'addSelectCalculated');
|
|
11
11
|
},
|
|
12
12
|
orderByCalculated(property, order, nulls, alias) {
|
|
13
|
-
return
|
|
13
|
+
return (0, calculated_column_utils_1.orderByCalculatedColumn)(this, 'orderBy', property, order, nulls, alias, 'orderByCalculated');
|
|
14
14
|
},
|
|
15
15
|
addOrderByCalculated(property, order, nulls, alias) {
|
|
16
|
-
return
|
|
16
|
+
return (0, calculated_column_utils_1.orderByCalculatedColumn)(this, 'addOrderBy', property, order, nulls, alias, 'addOrderByCalculated');
|
|
17
17
|
},
|
|
18
18
|
},
|
|
19
19
|
};
|
|
20
20
|
(0, prototype_utils_1.patchPrototype)(typeorm_1.SelectQueryBuilder, extension);
|
|
21
|
-
// implementation
|
|
22
|
-
function addSelectCalculated(builder, selection, property, aliasName) {
|
|
23
|
-
const { key } = (0, calculated_column_utils_1.resolveCalculatedColumn)(builder, property, aliasName, 'addSelectCalculated');
|
|
24
|
-
// two calls: addSelect's overloads take a string or a factory, not their union
|
|
25
|
-
if (typeof selection === 'function') {
|
|
26
|
-
return builder.addSelect(selection, key);
|
|
27
|
-
}
|
|
28
|
-
return builder.addSelect(selection, key);
|
|
29
|
-
}
|
|
30
|
-
function attachOrderByCalculated(builder, method, property, order, nulls, aliasName) {
|
|
31
|
-
const orderAlias = (0, calculated_column_utils_1.selectCalculatedForOrder)(builder, property, aliasName, `${method}Calculated`);
|
|
32
|
-
return builder[method](orderAlias, order, nulls);
|
|
33
|
-
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import { ObjectLiteral } from 'typeorm';
|
|
1
2
|
import type { CursorPaginationFilter } from '../types/interfaces/cursor-pagination.interface';
|
|
2
|
-
import type { GetManyWithCursorOptions, ListWithCursor } from '../types/extensions/cursor-pagination.types';
|
|
3
|
+
import type { GetManyWithCursorOptions, GetRawManyWithCursorOptions, ListWithCursor } from '../types/extensions/cursor-pagination.types';
|
|
3
4
|
export { InvalidCursorError } from '../utils/cursor.utils';
|
|
4
5
|
declare module 'typeorm/query-builder/SelectQueryBuilder' {
|
|
5
6
|
interface SelectQueryBuilder<Entity> {
|
|
@@ -14,6 +15,18 @@ declare module 'typeorm/query-builder/SelectQueryBuilder' {
|
|
|
14
15
|
* The query builder itself is not changed. Throws `InvalidCursorError` for a cursor that can't
|
|
15
16
|
* be decoded or was made for another order.
|
|
16
17
|
*/
|
|
17
|
-
getManyWithCursor(cursorFilter: CursorPaginationFilter | null | undefined, options: GetManyWithCursorOptions<
|
|
18
|
+
getManyWithCursor<CursorEntity extends ObjectLiteral = Entity>(this: SelectQueryBuilder<CursorEntity>, cursorFilter: CursorPaginationFilter | null | undefined, options: GetManyWithCursorOptions<CursorEntity>): Promise<ListWithCursor<CursorEntity>>;
|
|
19
|
+
/**
|
|
20
|
+
* `getManyWithCursor` for raw rows (`getRawMany()`), ordered by SQL expressions, so a computed
|
|
21
|
+
* value can be a key too: `CASE "member"."role" WHEN 'owner' THEN 0 ELSE 1 END`.
|
|
22
|
+
*
|
|
23
|
+
* Orders the query by `options.orderBy`, replacing its ORDER BY, and appends the primary key of
|
|
24
|
+
* the main alias when the query is on an entity and has no GROUP BY or DISTINCT. Loads
|
|
25
|
+
* `pageSize + 1` rows with LIMIT and returns `nextCursor` together with `hasMore`. The cursor
|
|
26
|
+
* names the keys by a hash of their SQL, not the SQL itself.
|
|
27
|
+
* The query builder itself is not changed. Throws `InvalidCursorError` for a cursor that can't
|
|
28
|
+
* be decoded or was made for another order.
|
|
29
|
+
*/
|
|
30
|
+
getRawManyWithCursor<Row = ObjectLiteral>(cursorFilter: CursorPaginationFilter | null | undefined, options: GetRawManyWithCursorOptions): Promise<ListWithCursor<Row>>;
|
|
18
31
|
}
|
|
19
32
|
}
|
|
@@ -2,11 +2,13 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.InvalidCursorError = void 0;
|
|
4
4
|
const SelectQueryBuilder_1 = require("typeorm/query-builder/SelectQueryBuilder");
|
|
5
|
-
const
|
|
5
|
+
const pagination_utils_1 = require("../utils/pagination.utils");
|
|
6
6
|
const prototype_utils_1 = require("../utils/prototype.utils");
|
|
7
7
|
const order_utils_1 = require("../utils/order.utils");
|
|
8
8
|
const where_utils_1 = require("../utils/where.utils");
|
|
9
9
|
const sql_utils_1 = require("../utils/sql.utils");
|
|
10
|
+
const query_builder_utils_1 = require("../utils/query-builder.utils");
|
|
11
|
+
const common_utils_1 = require("../utils/common.utils");
|
|
10
12
|
const cursor_utils_1 = require("../utils/cursor.utils");
|
|
11
13
|
var cursor_utils_2 = require("../utils/cursor.utils");
|
|
12
14
|
Object.defineProperty(exports, "InvalidCursorError", { enumerable: true, get: function () { return cursor_utils_2.InvalidCursorError; } });
|
|
@@ -16,6 +18,9 @@ const extension = {
|
|
|
16
18
|
getManyWithCursor(cursorFilter, options) {
|
|
17
19
|
return getManyWithCursor(this, cursorFilter, options);
|
|
18
20
|
},
|
|
21
|
+
getRawManyWithCursor(cursorFilter, options) {
|
|
22
|
+
return getRawManyWithCursor(this, cursorFilter, options);
|
|
23
|
+
},
|
|
19
24
|
},
|
|
20
25
|
};
|
|
21
26
|
(0, prototype_utils_1.patchPrototype)(SelectQueryBuilder_1.SelectQueryBuilder, extension);
|
|
@@ -44,14 +49,11 @@ function getCursorKeys(builder, orderBy) {
|
|
|
44
49
|
}
|
|
45
50
|
return keys;
|
|
46
51
|
}
|
|
47
|
-
const getPageSize = (pageSize) => typeof pageSize === 'number' && Number.isFinite(pageSize) && pageSize >= 1
|
|
48
|
-
? Math.floor(pageSize)
|
|
49
|
-
: constants_1.defaultPageSize;
|
|
50
52
|
async function getManyWithCursor(builder, cursorFilter, options) {
|
|
51
53
|
const { orderBy, useTakeAndSkip = false } = options !== null && options !== void 0 ? options : {};
|
|
52
54
|
const keys = getCursorKeys(builder, orderBy);
|
|
53
55
|
const fields = keys.map((key) => key.column.propertyPath);
|
|
54
|
-
const pageSize =
|
|
56
|
+
const pageSize = (0, pagination_utils_1.normalizePageSize)(cursorFilter === null || cursorFilter === void 0 ? void 0 : cursorFilter.pageSize);
|
|
55
57
|
const query = builder.clone();
|
|
56
58
|
const alias = query.alias;
|
|
57
59
|
const driver = query.connection.driver.options.type;
|
|
@@ -68,6 +70,7 @@ async function getManyWithCursor(builder, cursorFilter, options) {
|
|
|
68
70
|
}
|
|
69
71
|
query.orderBy();
|
|
70
72
|
keys.forEach((key) => query.addOrderBy(`${alias}.${key.column.propertyPath}`, key.direction));
|
|
73
|
+
const appliedOrderBy = (0, query_builder_utils_1.getAppliedOrderBy)(query);
|
|
71
74
|
// the key values of the last row are read from the raw result: dates and times as text, so the
|
|
72
75
|
// cursor keeps the database's precision (PostgreSQL timestamps have microseconds, a JS Date has not)
|
|
73
76
|
const keyAliases = keys.map((key, i) => {
|
|
@@ -81,16 +84,11 @@ async function getManyWithCursor(builder, cursorFilter, options) {
|
|
|
81
84
|
.map((key, i) => ({ key, i }))
|
|
82
85
|
.filter(({ key }) => key.column.isPrimary)
|
|
83
86
|
.map(({ key, i }) => ({ column: key.column, keyAlias: keyAliases[i] }));
|
|
84
|
-
|
|
85
|
-
query.take(pageSize + 1);
|
|
86
|
-
}
|
|
87
|
-
else {
|
|
88
|
-
query.limit(pageSize + 1);
|
|
89
|
-
}
|
|
87
|
+
(0, pagination_utils_1.setPageLimit)(query, pageSize + 1, useTakeAndSkip);
|
|
90
88
|
const { entities, raw } = await query.getRawAndEntities();
|
|
91
89
|
const list = entities.slice(0, pageSize);
|
|
92
90
|
if (entities.length <= pageSize) {
|
|
93
|
-
return { list, nextCursor: null, hasMore: false };
|
|
91
|
+
return { list, nextCursor: null, hasMore: false, pageSize, orderBy: appliedOrderBy };
|
|
94
92
|
}
|
|
95
93
|
// with joined collections there are several raw rows per entity: find one of the last entity's
|
|
96
94
|
const last = list[list.length - 1];
|
|
@@ -105,5 +103,84 @@ async function getManyWithCursor(builder, cursorFilter, options) {
|
|
|
105
103
|
}
|
|
106
104
|
return value;
|
|
107
105
|
});
|
|
108
|
-
return { list, nextCursor: (0, cursor_utils_1.encodeCursor)(fields, values), hasMore: true };
|
|
106
|
+
return { list, nextCursor: (0, cursor_utils_1.encodeCursor)(fields, values), hasMore: true, pageSize, orderBy: appliedOrderBy };
|
|
107
|
+
}
|
|
108
|
+
/** The expressions of `orderBy`, followed by the primary key of the main alias when it identifies a row. */
|
|
109
|
+
function getExpressionKeys(builder, orderBy) {
|
|
110
|
+
var _a;
|
|
111
|
+
const keys = (orderBy !== null && orderBy !== void 0 ? orderBy : []).map(({ selector, direction }) => {
|
|
112
|
+
if (typeof selector !== 'string' || !selector.trim()) {
|
|
113
|
+
throw new TypeError('getRawManyWithCursor: every orderBy key needs a selector');
|
|
114
|
+
}
|
|
115
|
+
return { selector, direction: (0, order_utils_1.toSqlOrderDirection)(direction) };
|
|
116
|
+
});
|
|
117
|
+
const { expressionMap } = builder;
|
|
118
|
+
const mainAlias = expressionMap.mainAlias;
|
|
119
|
+
// grouped or distinct rows are not one per entity, and their primary key may not even be selectable
|
|
120
|
+
const isRowPerEntity = expressionMap.groupBys.length === 0 &&
|
|
121
|
+
!expressionMap.selectDistinct &&
|
|
122
|
+
!((_a = expressionMap.selectDistinctOn) === null || _a === void 0 ? void 0 : _a.length);
|
|
123
|
+
if ((mainAlias === null || mainAlias === void 0 ? void 0 : mainAlias.hasMetadata) && isRowPerEntity) {
|
|
124
|
+
const lastDirection = keys.length ? keys[keys.length - 1].direction : 'ASC';
|
|
125
|
+
for (const column of mainAlias.metadata.primaryColumns) {
|
|
126
|
+
const quoted = `${builder.escape(mainAlias.name)}.${builder.escape(column.databaseName)}`;
|
|
127
|
+
const path = `${mainAlias.name}.${column.propertyPath}`;
|
|
128
|
+
if (!keys.some((key) => key.selector === quoted || key.selector === path)) {
|
|
129
|
+
keys.push({ selector: quoted, direction: lastDirection });
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
if (!keys.length) {
|
|
134
|
+
throw new TypeError('getRawManyWithCursor: orderBy is empty and the query has no primary key to order by');
|
|
135
|
+
}
|
|
136
|
+
return keys;
|
|
137
|
+
}
|
|
138
|
+
async function getRawManyWithCursor(builder, cursorFilter, options) {
|
|
139
|
+
const keys = getExpressionKeys(builder, options === null || options === void 0 ? void 0 : options.orderBy);
|
|
140
|
+
const fields = keys.map((key) => (0, cursor_utils_1.getExpressionKeyName)(key.selector));
|
|
141
|
+
const pageSize = (0, pagination_utils_1.normalizePageSize)(cursorFilter === null || cursorFilter === void 0 ? void 0 : cursorFilter.pageSize);
|
|
142
|
+
const query = builder.clone();
|
|
143
|
+
const driver = query.connection.driver.options.type;
|
|
144
|
+
const textCastType = (0, cursor_utils_1.getTextCastType)(driver);
|
|
145
|
+
if (cursorFilter === null || cursorFilter === void 0 ? void 0 : cursorFilter.cursor) {
|
|
146
|
+
const values = (0, cursor_utils_1.decodeCursor)(cursorFilter.cursor, fields);
|
|
147
|
+
const keysetColumns = keys.map((key, i) => {
|
|
148
|
+
const paramName = (0, sql_utils_1.createUniqueParameterName)(`cursor${i}`);
|
|
149
|
+
query.setParameter(paramName, values[i]);
|
|
150
|
+
// in parentheses: an expression may bind looser than the comparison
|
|
151
|
+
return { column: `(${key.selector})`, direction: key.direction, paramName };
|
|
152
|
+
});
|
|
153
|
+
(0, where_utils_1.andWhereNarrowing)(query, (0, cursor_utils_1.buildKeysetCondition)(keysetColumns, (0, cursor_utils_1.supportsRowValueComparison)(driver)));
|
|
154
|
+
}
|
|
155
|
+
query.orderBy();
|
|
156
|
+
keys.forEach((key) => query.addOrderBy(key.selector, key.direction));
|
|
157
|
+
const appliedOrderBy = (0, query_builder_utils_1.getAppliedOrderBy)(query);
|
|
158
|
+
// the type of an expression is unknown here: each key is also read as text, which the cursor
|
|
159
|
+
// takes instead when the driver returned a Date, so timestamps keep the database's precision
|
|
160
|
+
const keyAliases = keys.map((key, i) => {
|
|
161
|
+
const valueAlias = `cursor_key_${i}`;
|
|
162
|
+
const textAlias = textCastType ? `cursor_text_${i}` : undefined;
|
|
163
|
+
query.addSelect(key.selector, valueAlias);
|
|
164
|
+
if (textAlias) {
|
|
165
|
+
query.addSelect(`CAST((${key.selector}) AS ${textCastType})`, textAlias);
|
|
166
|
+
}
|
|
167
|
+
return { valueAlias, textAlias };
|
|
168
|
+
});
|
|
169
|
+
const addedAliases = new Set(keyAliases.flatMap(({ valueAlias, textAlias }) => (textAlias ? [valueAlias, textAlias] : [valueAlias])));
|
|
170
|
+
query.limit(pageSize + 1);
|
|
171
|
+
const rows = await query.getRawMany();
|
|
172
|
+
const page = rows.slice(0, pageSize);
|
|
173
|
+
const list = page.map((row) => Object.fromEntries(Object.entries(row).filter(([column]) => !addedAliases.has(column))));
|
|
174
|
+
if (rows.length <= pageSize) {
|
|
175
|
+
return { list, nextCursor: null, hasMore: false, pageSize, orderBy: appliedOrderBy };
|
|
176
|
+
}
|
|
177
|
+
const last = page[page.length - 1];
|
|
178
|
+
const values = keyAliases.map(({ valueAlias, textAlias }, i) => {
|
|
179
|
+
const value = last[valueAlias];
|
|
180
|
+
if (value === null || value === undefined) {
|
|
181
|
+
throw new TypeError(`getRawManyWithCursor: "${keys[i].selector}" is NULL in the last row; cursor keys must not be NULL`);
|
|
182
|
+
}
|
|
183
|
+
return (0, common_utils_1.isDate)(value) && textAlias ? last[textAlias] : value;
|
|
184
|
+
});
|
|
185
|
+
return { list, nextCursor: (0, cursor_utils_1.encodeCursor)(fields, values), hasMore: true, pageSize, orderBy: appliedOrderBy };
|
|
109
186
|
}
|
|
@@ -5,6 +5,7 @@ const SelectQueryBuilder_1 = require("typeorm/query-builder/SelectQueryBuilder")
|
|
|
5
5
|
const pagination_utils_1 = require("../utils/pagination.utils");
|
|
6
6
|
const constants_1 = require("../constants");
|
|
7
7
|
const prototype_utils_1 = require("../utils/prototype.utils");
|
|
8
|
+
const query_builder_utils_1 = require("../utils/query-builder.utils");
|
|
8
9
|
// patching
|
|
9
10
|
const extension = {
|
|
10
11
|
prototype: {
|
|
@@ -30,8 +31,7 @@ const extension = {
|
|
|
30
31
|
* runner, so the one that starts second finds a transaction active and does not open its own.
|
|
31
32
|
*/
|
|
32
33
|
function canCountConcurrently(builder) {
|
|
33
|
-
|
|
34
|
-
return !queryBuilder.queryRunner;
|
|
34
|
+
return !(0, query_builder_utils_1.getQueryRunner)(builder);
|
|
35
35
|
}
|
|
36
36
|
function getManyWithTotals(builder, paginationFilter, options) {
|
|
37
37
|
return readWithTotals(builder, paginationFilter, options, {
|
|
@@ -57,10 +57,9 @@ function getRawManyWithTotals(builder, paginationFilter, options) {
|
|
|
57
57
|
});
|
|
58
58
|
}
|
|
59
59
|
async function readWithTotals(builder, paginationFilter, options, read) {
|
|
60
|
-
var _a;
|
|
61
60
|
const { disableTotalsCalculation = false, loadAll = false, ...paginationOptions } = options || {};
|
|
62
|
-
const { limit, offset } = loadAll
|
|
63
|
-
? { limit: null, offset: null }
|
|
61
|
+
const { limit, offset, page, pageSize } = loadAll
|
|
62
|
+
? { limit: null, offset: null, page: null, pageSize: null }
|
|
64
63
|
: (0, pagination_utils_1.getLimitAndOffset)(paginationFilter);
|
|
65
64
|
// without a total, one row past the page is what tells whether more rows follow
|
|
66
65
|
const probeForMore = disableTotalsCalculation && !loadAll && limit !== null;
|
|
@@ -68,17 +67,11 @@ async function readWithTotals(builder, paginationFilter, options, read) {
|
|
|
68
67
|
if (loadAll) {
|
|
69
68
|
return read.rows(builder);
|
|
70
69
|
}
|
|
71
|
-
const paged =
|
|
70
|
+
const paged = (0, pagination_utils_1.paginateQuery)(builder, paginationFilter, paginationOptions);
|
|
72
71
|
if (probeForMore) {
|
|
73
|
-
const probeLimit = limit + 1;
|
|
74
72
|
const { useTakeAndSkip = constants_1.defaultUseTakeAndSkip } = paginationOptions;
|
|
75
|
-
// replaces the limit
|
|
76
|
-
|
|
77
|
-
paged.take(probeLimit);
|
|
78
|
-
}
|
|
79
|
-
else {
|
|
80
|
-
paged.limit(probeLimit);
|
|
81
|
-
}
|
|
73
|
+
// replaces the limit paginateQuery just set, keeping its offset
|
|
74
|
+
(0, pagination_utils_1.setPageLimit)(paged, limit + 1, useTakeAndSkip);
|
|
82
75
|
}
|
|
83
76
|
return read.rows(paged);
|
|
84
77
|
};
|
|
@@ -109,18 +102,15 @@ async function readWithTotals(builder, paginationFilter, options, read) {
|
|
|
109
102
|
hasMore =
|
|
110
103
|
total !== null && offset !== null ? offset + rows.length < total : null;
|
|
111
104
|
}
|
|
112
|
-
// allOrderBys, not orderBys: it is what the SQL is built from, so without an explicit order it
|
|
113
|
-
// falls back to the `orderBy` of @Entity, and respects the `disable-global-order` option
|
|
114
|
-
const orderBy = Object.entries((_a = builder.expressionMap.allOrderBys) !== null && _a !== void 0 ? _a : {}).map(([selector, value]) => typeof value === 'string'
|
|
115
|
-
? { selector, order: value }
|
|
116
|
-
: { selector, order: value.order, nulls: value.nulls });
|
|
117
105
|
return {
|
|
118
106
|
list,
|
|
119
107
|
total,
|
|
108
|
+
page,
|
|
109
|
+
pageSize,
|
|
120
110
|
limit,
|
|
121
111
|
offset,
|
|
122
112
|
hasMore,
|
|
123
|
-
orderBy,
|
|
113
|
+
orderBy: (0, query_builder_utils_1.getAppliedOrderBy)(builder),
|
|
124
114
|
};
|
|
125
115
|
}
|
|
126
116
|
/**
|
|
@@ -143,9 +133,6 @@ async function bothOrFirstError(first, second) {
|
|
|
143
133
|
function settle(promise) {
|
|
144
134
|
return promise.then((value) => ({ fulfilled: true, value }), (reason) => ({ fulfilled: false, reason }));
|
|
145
135
|
}
|
|
146
|
-
function getQueryRunner(builder) {
|
|
147
|
-
return builder.queryRunner;
|
|
148
|
-
}
|
|
149
136
|
/**
|
|
150
137
|
* Counts the rows of a raw query without its pagination and ORDER BY. The query goes into a
|
|
151
138
|
* subquery, so the count is that of its result rows whatever it groups or filters. A
|
|
@@ -154,13 +141,7 @@ function getQueryRunner(builder) {
|
|
|
154
141
|
*/
|
|
155
142
|
async function countRawRows(builder, countExpression) {
|
|
156
143
|
var _a;
|
|
157
|
-
const unpaged = cloneForCount(builder)
|
|
158
|
-
.orderBy()
|
|
159
|
-
.offset(undefined)
|
|
160
|
-
.limit(undefined)
|
|
161
|
-
.skip(undefined)
|
|
162
|
-
.take(undefined)
|
|
163
|
-
.setOption('disable-global-order');
|
|
144
|
+
const unpaged = (0, query_builder_utils_1.withoutPaging)(cloneForCount(builder));
|
|
164
145
|
let counting;
|
|
165
146
|
if (countExpression) {
|
|
166
147
|
// the expression counts on its own: DISTINCT ON next to an aggregate is an error
|
|
@@ -171,7 +152,7 @@ async function countRawRows(builder, countExpression) {
|
|
|
171
152
|
else {
|
|
172
153
|
// on the builder's query runner, if it has one: inside its transaction
|
|
173
154
|
const counter = builder.connection
|
|
174
|
-
.createQueryBuilder(getQueryRunner(builder))
|
|
155
|
+
.createQueryBuilder((0, query_builder_utils_1.getQueryRunner)(builder))
|
|
175
156
|
.select('COUNT(*)', 'count')
|
|
176
157
|
.from(`(${unpaged.getQuery()})`, 'counted')
|
|
177
158
|
.setParameters(unpaged.getParameters());
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const typeorm_1 = require("typeorm");
|
|
4
4
|
const parameter_utils_1 = require("../utils/parameter.utils");
|
|
5
|
+
const query_builder_utils_1 = require("../utils/query-builder.utils");
|
|
5
6
|
const prototype_utils_1 = require("../utils/prototype.utils");
|
|
6
7
|
/* eslint-enable @typescript-eslint/no-empty-interface, @typescript-eslint/no-unused-vars */
|
|
7
8
|
// patching
|
|
@@ -32,18 +33,15 @@ function toLateralSql(builder, subQuery) {
|
|
|
32
33
|
return { sql: `(${subQuery})`, parameters: {} };
|
|
33
34
|
}
|
|
34
35
|
const subBuilder = typeof subQuery === 'function' ? subQuery(detachedSubQuery(builder)) : subQuery;
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
const wrapped = subBuilder.expressionMap.subQuery ? query : `(${query})`;
|
|
38
|
-
const [sql, parameters] = (0, parameter_utils_1.withUniqueParameterNames)(wrapped, subBuilder.getParameters());
|
|
39
|
-
return { sql, parameters };
|
|
36
|
+
const [sql, parameters] = (0, parameter_utils_1.toSubQuerySql)(subBuilder);
|
|
37
|
+
return { sql: `(${sql})`, parameters };
|
|
40
38
|
}
|
|
41
39
|
/**
|
|
42
40
|
* A subquery builder for a factory, as TypeORM's `subQuery()` but without a parent: TypeORM's would
|
|
43
41
|
* set the factory's parameters on the outer query under their own names, before they can be renamed.
|
|
44
42
|
*/
|
|
45
43
|
function detachedSubQuery(builder) {
|
|
46
|
-
const queryRunner = builder
|
|
44
|
+
const queryRunner = (0, query_builder_utils_1.getQueryRunner)(builder);
|
|
47
45
|
const subQuery = builder.connection.createQueryBuilder(queryRunner);
|
|
48
46
|
subQuery.expressionMap.subQuery = true;
|
|
49
47
|
return subQuery;
|