Skip to content

Commit 83862b4

Browse files
authored
feat: add auto pk selection (#1071)
1 parent a5316d7 commit 83862b4

File tree

4 files changed

+43
-10
lines changed

4 files changed

+43
-10
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,7 @@ const paginateConfig: PaginateConfig<CatEntity> {
250250
* Default: None
251251
* Description: TypeORM partial selection. Limit selection further by using `select` query param.
252252
* https://typeorm.io/select-query-builder#partial-selection
253-
* Note: You must include the primary key in the selection.
253+
* Note: if you do not contain the primary key in the select array, primary key will be added automatically.
254254
*/
255255
select: ['id', 'name', 'color'],
256256

src/helper.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,30 @@ export function includesAllPrimaryKeyColumns(qb: SelectQueryBuilder<unknown>, pr
153153
.every((column) => propertyPath.includes(column))
154154
}
155155

156+
export function getPrimaryKeyColumns(qb: SelectQueryBuilder<any>, entityName?: string): string[] {
157+
return qb.expressionMap.mainAlias?.metadata?.primaryColumns.map((column) =>
158+
entityName ? `${entityName}.${column.propertyName}` : column.propertyName
159+
)
160+
}
161+
162+
export function getMissingPrimaryKeyColumns(qb: SelectQueryBuilder<any>, transformedCols: string[]): string[] {
163+
if (!transformedCols || transformedCols.length === 0) return []
164+
165+
const mainEntityPrimaryKeys = getPrimaryKeyColumns(qb)
166+
const missingPrimaryKeys: string[] = []
167+
168+
for (const pk of mainEntityPrimaryKeys) {
169+
const columnProperties = getPropertiesByColumnName(pk)
170+
const pkAlias = fixColumnAlias(columnProperties, qb.alias, false)
171+
172+
if (!transformedCols.includes(pkAlias)) {
173+
missingPrimaryKeys.push(pkAlias)
174+
}
175+
}
176+
177+
return missingPrimaryKeys
178+
}
179+
156180
export function hasColumnWithPropertyPath(
157181
qb: SelectQueryBuilder<unknown>,
158182
columnProperties: ColumnProperties

src/paginate.spec.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2515,7 +2515,7 @@ describe('paginate', () => {
25152515
)
25162516
})
25172517

2518-
it("should return all columns if select doesn't contain all primary columns", async () => {
2518+
it("should return primary columns if select doesn't contain all primary columns", async () => {
25192519
const config: PaginateConfig<CatEntity> = {
25202520
sortableColumns: ['id', 'name'],
25212521
select: ['name'],
@@ -2526,7 +2526,10 @@ describe('paginate', () => {
25262526

25272527
const result = await paginate<CatEntity>(query, catRepo, config)
25282528

2529-
expect(result.data).toStrictEqual(cats)
2529+
result.data.forEach((cat, index) => {
2530+
expect(cat.id).toBe(cats[index].id)
2531+
expect(cat.name).toBe(cats[index].name)
2532+
})
25302533
expect(result.meta.select).toStrictEqual(undefined)
25312534
expect(result.links.current).toBe('?page=1&limit=20&sortBy=id:ASC')
25322535
})

src/paginate.ts

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,10 @@ import {
1212
createRelationSchema,
1313
extractVirtualProperty,
1414
fixColumnAlias,
15+
getMissingPrimaryKeyColumns,
1516
getPaddedExpr,
1617
getPropertiesByColumnName,
1718
getQueryUrlComponents,
18-
includesAllPrimaryKeyColumns,
1919
isDateColumnType,
2020
isEntityKey,
2121
isFindOperator,
@@ -566,20 +566,26 @@ export async function paginate<T extends ObjectLiteral>(
566566

567567
// When we partial select the columns (main or relation) we must add the primary key column otherwise
568568
// typeorm will not be able to map the result.
569-
let selectParams =
569+
// so, we check if the selected columns are a subset of the primary key columns
570+
// and add the missing ones.
571+
const selectParams =
570572
config.select && query.select && !config.ignoreSelectInQueryParam
571573
? config.select.filter((column) => query.select.includes(column))
572574
: config.select
573-
if (!includesAllPrimaryKeyColumns(queryBuilder, query.select)) {
574-
selectParams = config.select
575-
}
576-
if (selectParams?.length > 0 && includesAllPrimaryKeyColumns(queryBuilder, selectParams)) {
577-
const cols: string[] = selectParams.reduce((cols, currentCol) => {
575+
576+
if (selectParams?.length > 0) {
577+
let cols: string[] = selectParams.reduce((cols, currentCol) => {
578578
const columnProperties = getPropertiesByColumnName(currentCol)
579579
const isRelation = checkIsRelation(queryBuilder, columnProperties.propertyPath)
580580
cols.push(fixColumnAlias(columnProperties, queryBuilder.alias, isRelation))
581581
return cols
582582
}, [])
583+
584+
const missingPrimaryKeys = getMissingPrimaryKeyColumns(queryBuilder, cols)
585+
if (missingPrimaryKeys.length > 0) {
586+
cols = cols.concat(missingPrimaryKeys)
587+
}
588+
583589
queryBuilder.select(cols)
584590
}
585591

0 commit comments

Comments
 (0)