bigal 15.3.0 → 15.4.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 +6 -0
- package/README.md +147 -18
- package/dist/index.cjs +28 -4
- package/dist/index.d.cts +18 -1
- package/dist/index.d.mts +18 -1
- package/dist/index.d.ts +18 -1
- package/dist/index.mjs +28 -4
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
# [15.4.0](https://github.com/bigalorm/bigal/compare/v15.3.0...v15.4.0) (2026-01-08)
|
|
2
|
+
|
|
3
|
+
### Features
|
|
4
|
+
|
|
5
|
+
- Add withCount() for efficient pagination with total count ([#266](https://github.com/bigalorm/bigal/issues/266)) ([233221e](https://github.com/bigalorm/bigal/commit/233221e7902af8ca3a4b24d1f60f0857bcd7803f))
|
|
6
|
+
|
|
1
7
|
# [15.3.0](https://github.com/bigalorm/bigal/compare/v15.2.0...v15.3.0) (2026-01-08)
|
|
2
8
|
|
|
3
9
|
### Features
|
package/README.md
CHANGED
|
@@ -325,14 +325,109 @@ const item = await ProductRepository.find({
|
|
|
325
325
|
});
|
|
326
326
|
```
|
|
327
327
|
|
|
328
|
-
####
|
|
328
|
+
#### String matching operators
|
|
329
|
+
|
|
330
|
+
BigAl provides four string matching operators. All use case-insensitive matching (PostgreSQL `ILIKE`) and accept arrays for OR matching.
|
|
331
|
+
|
|
332
|
+
| Operator | Description | SQL Pattern |
|
|
333
|
+
| ------------ | --------------------------------------------- | ----------- |
|
|
334
|
+
| `like` | Raw ILIKE pattern with your own `%` wildcards | As provided |
|
|
335
|
+
| `contains` | Substring match anywhere in the string | `%value%` |
|
|
336
|
+
| `startsWith` | Matches strings starting with the value | `value%` |
|
|
337
|
+
| `endsWith` | Matches strings ending with the value | `%value` |
|
|
329
338
|
|
|
330
339
|
```ts
|
|
340
|
+
const items = await ProductRepository.find().where({
|
|
341
|
+
name: { contains: 'widget' },
|
|
342
|
+
});
|
|
343
|
+
// SQL: SELECT ... FROM product WHERE name ILIKE '%widget%'
|
|
344
|
+
|
|
345
|
+
const items = await ProductRepository.find().where({
|
|
346
|
+
name: { startsWith: 'Pro' },
|
|
347
|
+
});
|
|
348
|
+
// SQL: SELECT ... FROM product WHERE name ILIKE 'Pro%'
|
|
349
|
+
|
|
350
|
+
const items = await ProductRepository.find().where({
|
|
351
|
+
name: { endsWith: 'ter' },
|
|
352
|
+
});
|
|
353
|
+
// SQL: SELECT ... FROM product WHERE name ILIKE '%ter'
|
|
354
|
+
|
|
355
|
+
const items = await ProductRepository.find().where({
|
|
356
|
+
name: { like: 'Pro%Widget%' },
|
|
357
|
+
});
|
|
358
|
+
// SQL: SELECT ... FROM product WHERE name ILIKE 'Pro%Widget%'
|
|
359
|
+
|
|
360
|
+
// Arrays create OR conditions
|
|
331
361
|
const items = await PersonRepository.find().where({
|
|
332
|
-
firstName: {
|
|
333
|
-
|
|
334
|
-
|
|
362
|
+
firstName: { like: ['walter', 'Jess%'] },
|
|
363
|
+
});
|
|
364
|
+
// SQL: SELECT ... FROM person WHERE (first_name ILIKE 'walter' OR first_name ILIKE 'Jess%')
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
#### Comparison operators
|
|
368
|
+
|
|
369
|
+
For number and date fields, use comparison operators:
|
|
370
|
+
|
|
371
|
+
| Operator | Description |
|
|
372
|
+
| -------- | ------------------------ |
|
|
373
|
+
| `<` | Less than |
|
|
374
|
+
| `<=` | Less than or equal to |
|
|
375
|
+
| `>` | Greater than |
|
|
376
|
+
| `>=` | Greater than or equal to |
|
|
377
|
+
|
|
378
|
+
```ts
|
|
379
|
+
const items = await ProductRepository.find().where({
|
|
380
|
+
price: { '>=': 100 },
|
|
381
|
+
});
|
|
382
|
+
// SQL: SELECT ... FROM product WHERE price >= $1
|
|
383
|
+
|
|
384
|
+
// Multiple operators on same field create AND
|
|
385
|
+
const items = await ProductRepository.find().where({
|
|
386
|
+
createdAt: { '>=': startDate, '<': endDate },
|
|
387
|
+
});
|
|
388
|
+
// SQL: SELECT ... FROM product WHERE created_at >= $1 AND created_at < $2
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
#### Array values (OR conditions)
|
|
392
|
+
|
|
393
|
+
When you pass an array of values, BigAl creates an OR condition:
|
|
394
|
+
|
|
395
|
+
```ts
|
|
396
|
+
const items = await PersonRepository.find().where({
|
|
397
|
+
age: [22, 23, 24],
|
|
398
|
+
});
|
|
399
|
+
// SQL: SELECT ... FROM person WHERE age IN ($1, $2, $3)
|
|
400
|
+
|
|
401
|
+
const items = await ProductRepository.find().where({
|
|
402
|
+
name: { startsWith: ['Pro', 'Pre'] },
|
|
403
|
+
});
|
|
404
|
+
// SQL: SELECT ... FROM product WHERE (name ILIKE 'Pro%' OR name ILIKE 'Pre%')
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
#### Negation operator (`!`)
|
|
408
|
+
|
|
409
|
+
Use `!` to negate any condition:
|
|
410
|
+
|
|
411
|
+
```ts
|
|
412
|
+
const items = await ProductRepository.find().where({
|
|
413
|
+
status: { '!': 'discontinued' },
|
|
414
|
+
});
|
|
415
|
+
// SQL: SELECT ... FROM product WHERE status <> $1
|
|
416
|
+
|
|
417
|
+
const items = await ProductRepository.find().where({
|
|
418
|
+
status: { '!': ['discontinued', 'archived'] },
|
|
419
|
+
});
|
|
420
|
+
// SQL: SELECT ... FROM product WHERE status NOT IN ($1, $2)
|
|
421
|
+
|
|
422
|
+
const items = await ProductRepository.find().where({
|
|
423
|
+
name: { '!': { startsWith: 'Test' } },
|
|
335
424
|
});
|
|
425
|
+
// SQL: SELECT ... FROM product WHERE name NOT ILIKE 'Test%'
|
|
426
|
+
|
|
427
|
+
const items = await ProductRepository.find().where({
|
|
428
|
+
deletedAt: { '!': null },
|
|
429
|
+
});
|
|
430
|
+
// SQL: SELECT ... FROM product WHERE deleted_at IS NOT NULL
|
|
336
431
|
```
|
|
337
432
|
|
|
338
433
|
#### Example of an AND statement
|
|
@@ -392,22 +487,28 @@ Equivalent to:
|
|
|
392
487
|
select * from person where ((first_name = $1) OR (last_name = $2)) AND ((first_name = $3) OR (last_name = $4))
|
|
393
488
|
```
|
|
394
489
|
|
|
395
|
-
####
|
|
490
|
+
#### Sorting results
|
|
491
|
+
|
|
492
|
+
Use `.sort()` to order results. Two syntax options are available:
|
|
493
|
+
|
|
494
|
+
**String syntax** - Use `asc` or `desc` (comma-separated for multiple columns):
|
|
396
495
|
|
|
397
496
|
```ts
|
|
398
|
-
const items = await PersonRepository.find()
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
497
|
+
const items = await PersonRepository.find().where({ lastName: 'Smith' }).sort('age asc');
|
|
498
|
+
// SQL: SELECT ... FROM person WHERE last_name = $1 ORDER BY age ASC
|
|
499
|
+
|
|
500
|
+
const items = await PersonRepository.find().where({ lastName: 'Smith' }).sort('age asc, createdAt desc');
|
|
501
|
+
// SQL: SELECT ... FROM person WHERE last_name = $1 ORDER BY age ASC, created_at DESC
|
|
502
|
+
```
|
|
503
|
+
|
|
504
|
+
**Object syntax** - Use `1` for ascending, `-1` for descending:
|
|
505
|
+
|
|
506
|
+
```ts
|
|
507
|
+
const items = await PersonRepository.find().where({ lastName: 'Smith' }).sort({ age: 1 });
|
|
508
|
+
// SQL: SELECT ... FROM person WHERE last_name = $1 ORDER BY age ASC
|
|
509
|
+
|
|
510
|
+
const items = await PersonRepository.find().where({ lastName: 'Smith' }).sort({ age: 1, createdAt: -1 });
|
|
511
|
+
// SQL: SELECT ... FROM person WHERE last_name = $1 ORDER BY age ASC, created_at DESC
|
|
411
512
|
```
|
|
412
513
|
|
|
413
514
|
#### Limit number results returned
|
|
@@ -460,6 +561,34 @@ const items = await FooRepository.find()
|
|
|
460
561
|
.paginate(page, pageSize);
|
|
461
562
|
```
|
|
462
563
|
|
|
564
|
+
#### Page results with total count using `withCount`
|
|
565
|
+
|
|
566
|
+
Use `.withCount()` to get both paginated results and the total count of matching records in a single query. This uses PostgreSQL's `COUNT(*) OVER()` window function for efficient execution.
|
|
567
|
+
|
|
568
|
+
```ts
|
|
569
|
+
const { results, totalCount } = await ProductRepository.find()
|
|
570
|
+
.where({
|
|
571
|
+
store: storeId,
|
|
572
|
+
})
|
|
573
|
+
.sort('name')
|
|
574
|
+
.limit(10)
|
|
575
|
+
.skip(20)
|
|
576
|
+
.withCount();
|
|
577
|
+
|
|
578
|
+
// results: Product[] (10 items from offset 20)
|
|
579
|
+
// totalCount: number (total matching products, ignoring LIMIT/OFFSET)
|
|
580
|
+
```
|
|
581
|
+
|
|
582
|
+
This is useful for building paginated UIs where you need to display total pages:
|
|
583
|
+
|
|
584
|
+
```ts
|
|
585
|
+
const page = 3;
|
|
586
|
+
const pageSize = 25;
|
|
587
|
+
const { results, totalCount } = await ProductRepository.find().where({ isActive: true }).paginate({ page, limit: pageSize }).withCount();
|
|
588
|
+
|
|
589
|
+
const totalPages = Math.ceil(totalCount / pageSize);
|
|
590
|
+
```
|
|
591
|
+
|
|
463
592
|
#### Join related tables
|
|
464
593
|
|
|
465
594
|
Use `join()` for INNER JOIN or `leftJoin()` for LEFT JOIN to filter or sort by related table columns in a single query.
|
package/dist/index.cjs
CHANGED
|
@@ -445,13 +445,17 @@ function getSelectQueryAndParams({
|
|
|
445
445
|
sorts,
|
|
446
446
|
skip,
|
|
447
447
|
limit,
|
|
448
|
-
joins
|
|
448
|
+
joins,
|
|
449
|
+
includeCount
|
|
449
450
|
}) {
|
|
450
451
|
let query = "SELECT ";
|
|
451
452
|
query += getColumnsToSelect({
|
|
452
453
|
model,
|
|
453
454
|
select
|
|
454
455
|
});
|
|
456
|
+
if (includeCount) {
|
|
457
|
+
query += ',count(*) OVER() AS "__total_count__"';
|
|
458
|
+
}
|
|
455
459
|
query += ` FROM ${model.qualifiedTableName}`;
|
|
456
460
|
const params = [];
|
|
457
461
|
if (joins?.length) {
|
|
@@ -901,7 +905,7 @@ function buildJoinClauses({
|
|
|
901
905
|
if (alias !== relatedModel.tableName) {
|
|
902
906
|
joinSql += ` AS "${alias}"`;
|
|
903
907
|
}
|
|
904
|
-
joinSql += ` ON ${model.qualifiedTableName}."${column.name}"
|
|
908
|
+
joinSql += ` ON ${model.qualifiedTableName}."${column.name}"="${alias}"."${relatedPrimaryKey.name}"`;
|
|
905
909
|
if (join.on) {
|
|
906
910
|
for (const [propertyName, value] of Object.entries(join.on)) {
|
|
907
911
|
const onColumn = relatedModel.columnsByPropertyName[propertyName];
|
|
@@ -2145,6 +2149,7 @@ ${stack ?? ""}`;
|
|
|
2145
2149
|
const populates = [];
|
|
2146
2150
|
const sorts = sort ? this._convertSortsToOrderBy(sort) : [];
|
|
2147
2151
|
const joins = [];
|
|
2152
|
+
let includeCount = false;
|
|
2148
2153
|
const modelInstance = this;
|
|
2149
2154
|
return {
|
|
2150
2155
|
/**
|
|
@@ -2255,6 +2260,10 @@ ${stack ?? ""}`;
|
|
|
2255
2260
|
const safePage = Math.max(page, 1);
|
|
2256
2261
|
return this.skip(safePage * paginateLimit - paginateLimit).limit(paginateLimit);
|
|
2257
2262
|
},
|
|
2263
|
+
withCount() {
|
|
2264
|
+
includeCount = true;
|
|
2265
|
+
return this;
|
|
2266
|
+
},
|
|
2258
2267
|
async then(resolve, reject) {
|
|
2259
2268
|
try {
|
|
2260
2269
|
if (typeof where === "string") {
|
|
@@ -2268,14 +2277,29 @@ ${stack ?? ""}`;
|
|
|
2268
2277
|
sorts,
|
|
2269
2278
|
skip: skip ?? 0,
|
|
2270
2279
|
limit: limit ?? 0,
|
|
2271
|
-
joins
|
|
2280
|
+
joins,
|
|
2281
|
+
includeCount
|
|
2272
2282
|
});
|
|
2273
2283
|
const pool = poolOverride ?? modelInstance._readonlyPool;
|
|
2274
2284
|
const results = await pool.query(query, params);
|
|
2275
|
-
|
|
2285
|
+
let totalCount = 0;
|
|
2286
|
+
if (includeCount && results.rows.length > 0 && results.rows[0]?.__total_count__ !== void 0) {
|
|
2287
|
+
totalCount = Number(results.rows[0].__total_count__);
|
|
2288
|
+
}
|
|
2289
|
+
const rows = includeCount ? results.rows.map((row) => {
|
|
2290
|
+
const { __total_count__, ...rest } = row;
|
|
2291
|
+
return rest;
|
|
2292
|
+
}) : results.rows;
|
|
2293
|
+
const entities = modelInstance._buildInstances(rows);
|
|
2276
2294
|
if (populates.length) {
|
|
2277
2295
|
await modelInstance.populateFields(entities, populates);
|
|
2278
2296
|
}
|
|
2297
|
+
if (includeCount) {
|
|
2298
|
+
return await resolve({
|
|
2299
|
+
results: entities,
|
|
2300
|
+
totalCount
|
|
2301
|
+
});
|
|
2302
|
+
}
|
|
2279
2303
|
return await resolve(entities);
|
|
2280
2304
|
} catch (ex) {
|
|
2281
2305
|
const typedException = ex;
|
package/dist/index.d.cts
CHANGED
|
@@ -569,6 +569,22 @@ interface PaginateOptions {
|
|
|
569
569
|
page: number;
|
|
570
570
|
}
|
|
571
571
|
|
|
572
|
+
interface FindWithCountResult<TReturn> {
|
|
573
|
+
results: TReturn[];
|
|
574
|
+
totalCount: number;
|
|
575
|
+
}
|
|
576
|
+
interface FindQueryWithCount<T extends Entity, TReturn, TJoins extends JoinInfo = never> extends PromiseLike<FindWithCountResult<TReturn>> {
|
|
577
|
+
select<TKeys extends string & keyof T>(keys: TKeys[]): FindQueryWithCount<T, Pick<T, TKeys>, TJoins>;
|
|
578
|
+
where(args: JoinedWhereQuery<T, TJoins>): FindQueryWithCount<T, TReturn, TJoins>;
|
|
579
|
+
populate<TProperty extends string & keyof PickByValueType<T, Entity> & keyof T, TPopulateType extends GetValueType<T[TProperty], Entity>, TPopulateSelectKeys extends string & keyof TPopulateType>(propertyName: TProperty, options?: PopulateArgs<TPopulateType, TPopulateSelectKeys>): FindQueryWithCount<T, Omit<TReturn, TProperty> & Populated<T, TProperty, TPopulateType, TPopulateSelectKeys>, TJoins>;
|
|
580
|
+
join<TProperty extends ModelRelationshipKeys<T>, TAlias extends string = TProperty>(propertyName: TProperty, alias?: TAlias): FindQueryWithCount<T, TReturn, JoinInfo<TProperty, TAlias, GetValueType<T[TProperty], Entity>> | TJoins>;
|
|
581
|
+
leftJoin<TProperty extends ModelRelationshipKeys<T>, TAlias extends string = TProperty>(propertyName: TProperty, alias?: TAlias, on?: WhereQuery<GetValueType<T[TProperty], Entity>>): FindQueryWithCount<T, TReturn, JoinInfo<TProperty, TAlias, GetValueType<T[TProperty], Entity>> | TJoins>;
|
|
582
|
+
sort(value?: JoinedSort<T, TJoins>): FindQueryWithCount<T, TReturn, TJoins>;
|
|
583
|
+
limit(value: number): FindQueryWithCount<T, TReturn, TJoins>;
|
|
584
|
+
skip(value: number): FindQueryWithCount<T, TReturn, TJoins>;
|
|
585
|
+
paginate(options: PaginateOptions): FindQueryWithCount<T, TReturn, TJoins>;
|
|
586
|
+
}
|
|
587
|
+
|
|
572
588
|
interface FindResult<T extends Entity, TReturn, TJoins extends JoinInfo = never> extends PromiseLike<TReturn[]> {
|
|
573
589
|
select<TKeys extends string & keyof T>(keys: TKeys[]): FindResult<T, Pick<T, TKeys>, TJoins>;
|
|
574
590
|
where(args: JoinedWhereQuery<T, TJoins>): FindResult<T, TReturn, TJoins>;
|
|
@@ -579,6 +595,7 @@ interface FindResult<T extends Entity, TReturn, TJoins extends JoinInfo = never>
|
|
|
579
595
|
limit(value: number): FindResult<T, TReturn, TJoins>;
|
|
580
596
|
skip(value: number): FindResult<T, TReturn, TJoins>;
|
|
581
597
|
paginate(options: PaginateOptions): FindResult<T, TReturn, TJoins>;
|
|
598
|
+
withCount(): FindQueryWithCount<T, TReturn, TJoins>;
|
|
582
599
|
UNSAFE_withOriginalFieldType<TProperty extends string & keyof PickByValueType<T, Entity> & keyof T>(propertyName: TProperty): FindResult<T, Omit<TReturn, TProperty> & Pick<T, TProperty>, TJoins>;
|
|
583
600
|
}
|
|
584
601
|
|
|
@@ -1029,4 +1046,4 @@ interface InitializeOptions extends IConnection {
|
|
|
1029
1046
|
declare function initialize({ models, pool, readonlyPool, connections, expose }: InitializeOptions): Record<string, IReadonlyRepository<Entity> | IRepository<Entity>>;
|
|
1030
1047
|
|
|
1031
1048
|
export { ColumnBaseMetadata, ColumnCollectionMetadata, ColumnModelMetadata, ColumnTypeMetadata, Entity, ModelMetadata, QueryError, ReadonlyRepository, Repository, ScalarSubquery, SubqueryBuilder, column, createDateColumn, getMetadataStorage, initialize, primaryColumn, subquery, table, updateDateColumn, versionColumn };
|
|
1032
|
-
export type { ClassLike, ColumnBaseMetadataOptions, ColumnCollectionMetadataOptions, ColumnMetadata, ColumnModelMetadataOptions, ColumnModifierMetadata, ColumnTypeMetadataOptions, Comparer, CountResult, CreateUpdateOptions, CreateUpdateParams, DeleteOptions, DestroyResult, DoNotReturnRecords, EntityFieldValue, EntityPrimitiveOrId, EntityStatic, ExcludeEntityCollections, ExcludeFunctions, FindArgs, FindOneArgs, FindOneResult, FindResult, GetValueType, IConnection, IReadonlyRepository, IRepository, IRepositoryOptions, IncludeFunctions, InitializeOptions, IsValueOfType, JoinDefinition, JoinInfo, JoinType, JoinedSort, JoinedWhereQuery, LiteralValues, ModelMetadataOptions, ModelRelationshipKeys, MultipleSortString, NegatableConstraint, NotEntity, NotEntityBrand, NumberOrDateConstraint, NumberOrDateConstraintWithSubquery, OmitEntityCollections, OmitFunctions, OrderBy, PaginateOptions, PickAsType, PickByValueType, PickFunctions, PoolLike, PoolQueryResult, PopulateArgs, Populated, QueryResult, QueryResultOptionalPopulated, QueryResultPopulated, QueryResultRow, ReturnSelect$1 as ReturnSelect, ScalarSubqueryConstraint, Sort, SortObject, SortObjectValue, SortString, StringConstraint, SubqueryBuilderLike, SubqueryInConstraint, WhereClauseValue, WhereQuery, WhereQueryStatement };
|
|
1049
|
+
export type { ClassLike, ColumnBaseMetadataOptions, ColumnCollectionMetadataOptions, ColumnMetadata, ColumnModelMetadataOptions, ColumnModifierMetadata, ColumnTypeMetadataOptions, Comparer, CountResult, CreateUpdateOptions, CreateUpdateParams, DeleteOptions, DestroyResult, DoNotReturnRecords, EntityFieldValue, EntityPrimitiveOrId, EntityStatic, ExcludeEntityCollections, ExcludeFunctions, FindArgs, FindOneArgs, FindOneResult, FindQueryWithCount, FindResult, FindWithCountResult, GetValueType, IConnection, IReadonlyRepository, IRepository, IRepositoryOptions, IncludeFunctions, InitializeOptions, IsValueOfType, JoinDefinition, JoinInfo, JoinType, JoinedSort, JoinedWhereQuery, LiteralValues, ModelMetadataOptions, ModelRelationshipKeys, MultipleSortString, NegatableConstraint, NotEntity, NotEntityBrand, NumberOrDateConstraint, NumberOrDateConstraintWithSubquery, OmitEntityCollections, OmitFunctions, OrderBy, PaginateOptions, PickAsType, PickByValueType, PickFunctions, PoolLike, PoolQueryResult, PopulateArgs, Populated, QueryResult, QueryResultOptionalPopulated, QueryResultPopulated, QueryResultRow, ReturnSelect$1 as ReturnSelect, ScalarSubqueryConstraint, Sort, SortObject, SortObjectValue, SortString, StringConstraint, SubqueryBuilderLike, SubqueryInConstraint, WhereClauseValue, WhereQuery, WhereQueryStatement };
|
package/dist/index.d.mts
CHANGED
|
@@ -569,6 +569,22 @@ interface PaginateOptions {
|
|
|
569
569
|
page: number;
|
|
570
570
|
}
|
|
571
571
|
|
|
572
|
+
interface FindWithCountResult<TReturn> {
|
|
573
|
+
results: TReturn[];
|
|
574
|
+
totalCount: number;
|
|
575
|
+
}
|
|
576
|
+
interface FindQueryWithCount<T extends Entity, TReturn, TJoins extends JoinInfo = never> extends PromiseLike<FindWithCountResult<TReturn>> {
|
|
577
|
+
select<TKeys extends string & keyof T>(keys: TKeys[]): FindQueryWithCount<T, Pick<T, TKeys>, TJoins>;
|
|
578
|
+
where(args: JoinedWhereQuery<T, TJoins>): FindQueryWithCount<T, TReturn, TJoins>;
|
|
579
|
+
populate<TProperty extends string & keyof PickByValueType<T, Entity> & keyof T, TPopulateType extends GetValueType<T[TProperty], Entity>, TPopulateSelectKeys extends string & keyof TPopulateType>(propertyName: TProperty, options?: PopulateArgs<TPopulateType, TPopulateSelectKeys>): FindQueryWithCount<T, Omit<TReturn, TProperty> & Populated<T, TProperty, TPopulateType, TPopulateSelectKeys>, TJoins>;
|
|
580
|
+
join<TProperty extends ModelRelationshipKeys<T>, TAlias extends string = TProperty>(propertyName: TProperty, alias?: TAlias): FindQueryWithCount<T, TReturn, JoinInfo<TProperty, TAlias, GetValueType<T[TProperty], Entity>> | TJoins>;
|
|
581
|
+
leftJoin<TProperty extends ModelRelationshipKeys<T>, TAlias extends string = TProperty>(propertyName: TProperty, alias?: TAlias, on?: WhereQuery<GetValueType<T[TProperty], Entity>>): FindQueryWithCount<T, TReturn, JoinInfo<TProperty, TAlias, GetValueType<T[TProperty], Entity>> | TJoins>;
|
|
582
|
+
sort(value?: JoinedSort<T, TJoins>): FindQueryWithCount<T, TReturn, TJoins>;
|
|
583
|
+
limit(value: number): FindQueryWithCount<T, TReturn, TJoins>;
|
|
584
|
+
skip(value: number): FindQueryWithCount<T, TReturn, TJoins>;
|
|
585
|
+
paginate(options: PaginateOptions): FindQueryWithCount<T, TReturn, TJoins>;
|
|
586
|
+
}
|
|
587
|
+
|
|
572
588
|
interface FindResult<T extends Entity, TReturn, TJoins extends JoinInfo = never> extends PromiseLike<TReturn[]> {
|
|
573
589
|
select<TKeys extends string & keyof T>(keys: TKeys[]): FindResult<T, Pick<T, TKeys>, TJoins>;
|
|
574
590
|
where(args: JoinedWhereQuery<T, TJoins>): FindResult<T, TReturn, TJoins>;
|
|
@@ -579,6 +595,7 @@ interface FindResult<T extends Entity, TReturn, TJoins extends JoinInfo = never>
|
|
|
579
595
|
limit(value: number): FindResult<T, TReturn, TJoins>;
|
|
580
596
|
skip(value: number): FindResult<T, TReturn, TJoins>;
|
|
581
597
|
paginate(options: PaginateOptions): FindResult<T, TReturn, TJoins>;
|
|
598
|
+
withCount(): FindQueryWithCount<T, TReturn, TJoins>;
|
|
582
599
|
UNSAFE_withOriginalFieldType<TProperty extends string & keyof PickByValueType<T, Entity> & keyof T>(propertyName: TProperty): FindResult<T, Omit<TReturn, TProperty> & Pick<T, TProperty>, TJoins>;
|
|
583
600
|
}
|
|
584
601
|
|
|
@@ -1029,4 +1046,4 @@ interface InitializeOptions extends IConnection {
|
|
|
1029
1046
|
declare function initialize({ models, pool, readonlyPool, connections, expose }: InitializeOptions): Record<string, IReadonlyRepository<Entity> | IRepository<Entity>>;
|
|
1030
1047
|
|
|
1031
1048
|
export { ColumnBaseMetadata, ColumnCollectionMetadata, ColumnModelMetadata, ColumnTypeMetadata, Entity, ModelMetadata, QueryError, ReadonlyRepository, Repository, ScalarSubquery, SubqueryBuilder, column, createDateColumn, getMetadataStorage, initialize, primaryColumn, subquery, table, updateDateColumn, versionColumn };
|
|
1032
|
-
export type { ClassLike, ColumnBaseMetadataOptions, ColumnCollectionMetadataOptions, ColumnMetadata, ColumnModelMetadataOptions, ColumnModifierMetadata, ColumnTypeMetadataOptions, Comparer, CountResult, CreateUpdateOptions, CreateUpdateParams, DeleteOptions, DestroyResult, DoNotReturnRecords, EntityFieldValue, EntityPrimitiveOrId, EntityStatic, ExcludeEntityCollections, ExcludeFunctions, FindArgs, FindOneArgs, FindOneResult, FindResult, GetValueType, IConnection, IReadonlyRepository, IRepository, IRepositoryOptions, IncludeFunctions, InitializeOptions, IsValueOfType, JoinDefinition, JoinInfo, JoinType, JoinedSort, JoinedWhereQuery, LiteralValues, ModelMetadataOptions, ModelRelationshipKeys, MultipleSortString, NegatableConstraint, NotEntity, NotEntityBrand, NumberOrDateConstraint, NumberOrDateConstraintWithSubquery, OmitEntityCollections, OmitFunctions, OrderBy, PaginateOptions, PickAsType, PickByValueType, PickFunctions, PoolLike, PoolQueryResult, PopulateArgs, Populated, QueryResult, QueryResultOptionalPopulated, QueryResultPopulated, QueryResultRow, ReturnSelect$1 as ReturnSelect, ScalarSubqueryConstraint, Sort, SortObject, SortObjectValue, SortString, StringConstraint, SubqueryBuilderLike, SubqueryInConstraint, WhereClauseValue, WhereQuery, WhereQueryStatement };
|
|
1049
|
+
export type { ClassLike, ColumnBaseMetadataOptions, ColumnCollectionMetadataOptions, ColumnMetadata, ColumnModelMetadataOptions, ColumnModifierMetadata, ColumnTypeMetadataOptions, Comparer, CountResult, CreateUpdateOptions, CreateUpdateParams, DeleteOptions, DestroyResult, DoNotReturnRecords, EntityFieldValue, EntityPrimitiveOrId, EntityStatic, ExcludeEntityCollections, ExcludeFunctions, FindArgs, FindOneArgs, FindOneResult, FindQueryWithCount, FindResult, FindWithCountResult, GetValueType, IConnection, IReadonlyRepository, IRepository, IRepositoryOptions, IncludeFunctions, InitializeOptions, IsValueOfType, JoinDefinition, JoinInfo, JoinType, JoinedSort, JoinedWhereQuery, LiteralValues, ModelMetadataOptions, ModelRelationshipKeys, MultipleSortString, NegatableConstraint, NotEntity, NotEntityBrand, NumberOrDateConstraint, NumberOrDateConstraintWithSubquery, OmitEntityCollections, OmitFunctions, OrderBy, PaginateOptions, PickAsType, PickByValueType, PickFunctions, PoolLike, PoolQueryResult, PopulateArgs, Populated, QueryResult, QueryResultOptionalPopulated, QueryResultPopulated, QueryResultRow, ReturnSelect$1 as ReturnSelect, ScalarSubqueryConstraint, Sort, SortObject, SortObjectValue, SortString, StringConstraint, SubqueryBuilderLike, SubqueryInConstraint, WhereClauseValue, WhereQuery, WhereQueryStatement };
|
package/dist/index.d.ts
CHANGED
|
@@ -569,6 +569,22 @@ interface PaginateOptions {
|
|
|
569
569
|
page: number;
|
|
570
570
|
}
|
|
571
571
|
|
|
572
|
+
interface FindWithCountResult<TReturn> {
|
|
573
|
+
results: TReturn[];
|
|
574
|
+
totalCount: number;
|
|
575
|
+
}
|
|
576
|
+
interface FindQueryWithCount<T extends Entity, TReturn, TJoins extends JoinInfo = never> extends PromiseLike<FindWithCountResult<TReturn>> {
|
|
577
|
+
select<TKeys extends string & keyof T>(keys: TKeys[]): FindQueryWithCount<T, Pick<T, TKeys>, TJoins>;
|
|
578
|
+
where(args: JoinedWhereQuery<T, TJoins>): FindQueryWithCount<T, TReturn, TJoins>;
|
|
579
|
+
populate<TProperty extends string & keyof PickByValueType<T, Entity> & keyof T, TPopulateType extends GetValueType<T[TProperty], Entity>, TPopulateSelectKeys extends string & keyof TPopulateType>(propertyName: TProperty, options?: PopulateArgs<TPopulateType, TPopulateSelectKeys>): FindQueryWithCount<T, Omit<TReturn, TProperty> & Populated<T, TProperty, TPopulateType, TPopulateSelectKeys>, TJoins>;
|
|
580
|
+
join<TProperty extends ModelRelationshipKeys<T>, TAlias extends string = TProperty>(propertyName: TProperty, alias?: TAlias): FindQueryWithCount<T, TReturn, JoinInfo<TProperty, TAlias, GetValueType<T[TProperty], Entity>> | TJoins>;
|
|
581
|
+
leftJoin<TProperty extends ModelRelationshipKeys<T>, TAlias extends string = TProperty>(propertyName: TProperty, alias?: TAlias, on?: WhereQuery<GetValueType<T[TProperty], Entity>>): FindQueryWithCount<T, TReturn, JoinInfo<TProperty, TAlias, GetValueType<T[TProperty], Entity>> | TJoins>;
|
|
582
|
+
sort(value?: JoinedSort<T, TJoins>): FindQueryWithCount<T, TReturn, TJoins>;
|
|
583
|
+
limit(value: number): FindQueryWithCount<T, TReturn, TJoins>;
|
|
584
|
+
skip(value: number): FindQueryWithCount<T, TReturn, TJoins>;
|
|
585
|
+
paginate(options: PaginateOptions): FindQueryWithCount<T, TReturn, TJoins>;
|
|
586
|
+
}
|
|
587
|
+
|
|
572
588
|
interface FindResult<T extends Entity, TReturn, TJoins extends JoinInfo = never> extends PromiseLike<TReturn[]> {
|
|
573
589
|
select<TKeys extends string & keyof T>(keys: TKeys[]): FindResult<T, Pick<T, TKeys>, TJoins>;
|
|
574
590
|
where(args: JoinedWhereQuery<T, TJoins>): FindResult<T, TReturn, TJoins>;
|
|
@@ -579,6 +595,7 @@ interface FindResult<T extends Entity, TReturn, TJoins extends JoinInfo = never>
|
|
|
579
595
|
limit(value: number): FindResult<T, TReturn, TJoins>;
|
|
580
596
|
skip(value: number): FindResult<T, TReturn, TJoins>;
|
|
581
597
|
paginate(options: PaginateOptions): FindResult<T, TReturn, TJoins>;
|
|
598
|
+
withCount(): FindQueryWithCount<T, TReturn, TJoins>;
|
|
582
599
|
UNSAFE_withOriginalFieldType<TProperty extends string & keyof PickByValueType<T, Entity> & keyof T>(propertyName: TProperty): FindResult<T, Omit<TReturn, TProperty> & Pick<T, TProperty>, TJoins>;
|
|
583
600
|
}
|
|
584
601
|
|
|
@@ -1029,4 +1046,4 @@ interface InitializeOptions extends IConnection {
|
|
|
1029
1046
|
declare function initialize({ models, pool, readonlyPool, connections, expose }: InitializeOptions): Record<string, IReadonlyRepository<Entity> | IRepository<Entity>>;
|
|
1030
1047
|
|
|
1031
1048
|
export { ColumnBaseMetadata, ColumnCollectionMetadata, ColumnModelMetadata, ColumnTypeMetadata, Entity, ModelMetadata, QueryError, ReadonlyRepository, Repository, ScalarSubquery, SubqueryBuilder, column, createDateColumn, getMetadataStorage, initialize, primaryColumn, subquery, table, updateDateColumn, versionColumn };
|
|
1032
|
-
export type { ClassLike, ColumnBaseMetadataOptions, ColumnCollectionMetadataOptions, ColumnMetadata, ColumnModelMetadataOptions, ColumnModifierMetadata, ColumnTypeMetadataOptions, Comparer, CountResult, CreateUpdateOptions, CreateUpdateParams, DeleteOptions, DestroyResult, DoNotReturnRecords, EntityFieldValue, EntityPrimitiveOrId, EntityStatic, ExcludeEntityCollections, ExcludeFunctions, FindArgs, FindOneArgs, FindOneResult, FindResult, GetValueType, IConnection, IReadonlyRepository, IRepository, IRepositoryOptions, IncludeFunctions, InitializeOptions, IsValueOfType, JoinDefinition, JoinInfo, JoinType, JoinedSort, JoinedWhereQuery, LiteralValues, ModelMetadataOptions, ModelRelationshipKeys, MultipleSortString, NegatableConstraint, NotEntity, NotEntityBrand, NumberOrDateConstraint, NumberOrDateConstraintWithSubquery, OmitEntityCollections, OmitFunctions, OrderBy, PaginateOptions, PickAsType, PickByValueType, PickFunctions, PoolLike, PoolQueryResult, PopulateArgs, Populated, QueryResult, QueryResultOptionalPopulated, QueryResultPopulated, QueryResultRow, ReturnSelect$1 as ReturnSelect, ScalarSubqueryConstraint, Sort, SortObject, SortObjectValue, SortString, StringConstraint, SubqueryBuilderLike, SubqueryInConstraint, WhereClauseValue, WhereQuery, WhereQueryStatement };
|
|
1049
|
+
export type { ClassLike, ColumnBaseMetadataOptions, ColumnCollectionMetadataOptions, ColumnMetadata, ColumnModelMetadataOptions, ColumnModifierMetadata, ColumnTypeMetadataOptions, Comparer, CountResult, CreateUpdateOptions, CreateUpdateParams, DeleteOptions, DestroyResult, DoNotReturnRecords, EntityFieldValue, EntityPrimitiveOrId, EntityStatic, ExcludeEntityCollections, ExcludeFunctions, FindArgs, FindOneArgs, FindOneResult, FindQueryWithCount, FindResult, FindWithCountResult, GetValueType, IConnection, IReadonlyRepository, IRepository, IRepositoryOptions, IncludeFunctions, InitializeOptions, IsValueOfType, JoinDefinition, JoinInfo, JoinType, JoinedSort, JoinedWhereQuery, LiteralValues, ModelMetadataOptions, ModelRelationshipKeys, MultipleSortString, NegatableConstraint, NotEntity, NotEntityBrand, NumberOrDateConstraint, NumberOrDateConstraintWithSubquery, OmitEntityCollections, OmitFunctions, OrderBy, PaginateOptions, PickAsType, PickByValueType, PickFunctions, PoolLike, PoolQueryResult, PopulateArgs, Populated, QueryResult, QueryResultOptionalPopulated, QueryResultPopulated, QueryResultRow, ReturnSelect$1 as ReturnSelect, ScalarSubqueryConstraint, Sort, SortObject, SortObjectValue, SortString, StringConstraint, SubqueryBuilderLike, SubqueryInConstraint, WhereClauseValue, WhereQuery, WhereQueryStatement };
|
package/dist/index.mjs
CHANGED
|
@@ -443,13 +443,17 @@ function getSelectQueryAndParams({
|
|
|
443
443
|
sorts,
|
|
444
444
|
skip,
|
|
445
445
|
limit,
|
|
446
|
-
joins
|
|
446
|
+
joins,
|
|
447
|
+
includeCount
|
|
447
448
|
}) {
|
|
448
449
|
let query = "SELECT ";
|
|
449
450
|
query += getColumnsToSelect({
|
|
450
451
|
model,
|
|
451
452
|
select
|
|
452
453
|
});
|
|
454
|
+
if (includeCount) {
|
|
455
|
+
query += ',count(*) OVER() AS "__total_count__"';
|
|
456
|
+
}
|
|
453
457
|
query += ` FROM ${model.qualifiedTableName}`;
|
|
454
458
|
const params = [];
|
|
455
459
|
if (joins?.length) {
|
|
@@ -899,7 +903,7 @@ function buildJoinClauses({
|
|
|
899
903
|
if (alias !== relatedModel.tableName) {
|
|
900
904
|
joinSql += ` AS "${alias}"`;
|
|
901
905
|
}
|
|
902
|
-
joinSql += ` ON ${model.qualifiedTableName}."${column.name}"
|
|
906
|
+
joinSql += ` ON ${model.qualifiedTableName}."${column.name}"="${alias}"."${relatedPrimaryKey.name}"`;
|
|
903
907
|
if (join.on) {
|
|
904
908
|
for (const [propertyName, value] of Object.entries(join.on)) {
|
|
905
909
|
const onColumn = relatedModel.columnsByPropertyName[propertyName];
|
|
@@ -2143,6 +2147,7 @@ ${stack ?? ""}`;
|
|
|
2143
2147
|
const populates = [];
|
|
2144
2148
|
const sorts = sort ? this._convertSortsToOrderBy(sort) : [];
|
|
2145
2149
|
const joins = [];
|
|
2150
|
+
let includeCount = false;
|
|
2146
2151
|
const modelInstance = this;
|
|
2147
2152
|
return {
|
|
2148
2153
|
/**
|
|
@@ -2253,6 +2258,10 @@ ${stack ?? ""}`;
|
|
|
2253
2258
|
const safePage = Math.max(page, 1);
|
|
2254
2259
|
return this.skip(safePage * paginateLimit - paginateLimit).limit(paginateLimit);
|
|
2255
2260
|
},
|
|
2261
|
+
withCount() {
|
|
2262
|
+
includeCount = true;
|
|
2263
|
+
return this;
|
|
2264
|
+
},
|
|
2256
2265
|
async then(resolve, reject) {
|
|
2257
2266
|
try {
|
|
2258
2267
|
if (typeof where === "string") {
|
|
@@ -2266,14 +2275,29 @@ ${stack ?? ""}`;
|
|
|
2266
2275
|
sorts,
|
|
2267
2276
|
skip: skip ?? 0,
|
|
2268
2277
|
limit: limit ?? 0,
|
|
2269
|
-
joins
|
|
2278
|
+
joins,
|
|
2279
|
+
includeCount
|
|
2270
2280
|
});
|
|
2271
2281
|
const pool = poolOverride ?? modelInstance._readonlyPool;
|
|
2272
2282
|
const results = await pool.query(query, params);
|
|
2273
|
-
|
|
2283
|
+
let totalCount = 0;
|
|
2284
|
+
if (includeCount && results.rows.length > 0 && results.rows[0]?.__total_count__ !== void 0) {
|
|
2285
|
+
totalCount = Number(results.rows[0].__total_count__);
|
|
2286
|
+
}
|
|
2287
|
+
const rows = includeCount ? results.rows.map((row) => {
|
|
2288
|
+
const { __total_count__, ...rest } = row;
|
|
2289
|
+
return rest;
|
|
2290
|
+
}) : results.rows;
|
|
2291
|
+
const entities = modelInstance._buildInstances(rows);
|
|
2274
2292
|
if (populates.length) {
|
|
2275
2293
|
await modelInstance.populateFields(entities, populates);
|
|
2276
2294
|
}
|
|
2295
|
+
if (includeCount) {
|
|
2296
|
+
return await resolve({
|
|
2297
|
+
results: entities,
|
|
2298
|
+
totalCount
|
|
2299
|
+
});
|
|
2300
|
+
}
|
|
2277
2301
|
return await resolve(entities);
|
|
2278
2302
|
} catch (ex) {
|
|
2279
2303
|
const typedException = ex;
|