Skip to content

Commit 9b686a3

Browse files
authored
fix: null sort (#1089)
1 parent fd8c36a commit 9b686a3

File tree

4 files changed

+40
-7
lines changed

4 files changed

+40
-7
lines changed

.nvmrc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
v20

src/helper.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -347,3 +347,7 @@ export function isDateColumnType(type: any): boolean {
347347
]
348348
return dateTypes.includes(type)
349349
}
350+
351+
export function quoteVirtualColumn(columnName: string, isMySqlOrMariaDb: boolean): string {
352+
return isMySqlOrMariaDb ? `\`${columnName}\`` : `"${columnName}"`
353+
}

src/paginate.spec.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -876,6 +876,33 @@ describe('paginate', () => {
876876
expect(result.links.current).toBe('?page=1&limit=20&sortBy=cat.id:DESC')
877877
})
878878

879+
it('should handle nullSort with relations properly', async () => {
880+
const config: PaginateConfig<CatEntity> = {
881+
sortableColumns: ['id', 'age'],
882+
nullSort: 'last',
883+
defaultSortBy: [['age', 'ASC']],
884+
relations: ['toys'],
885+
}
886+
const query: PaginateQuery = {
887+
path: '',
888+
}
889+
890+
const result = await paginate<CatEntity>(query, catRepo, config)
891+
892+
// Prepare expected result - cats ordered by age with null age last, including toys relation
893+
const expectedResult = [...cats]
894+
.sort((a, b) => {
895+
if (a.age === null && b.age === null) return 0
896+
if (a.age === null) return 1
897+
if (b.age === null) return -1
898+
return a.age - b.age
899+
})
900+
.map((cat) => cat.id)
901+
902+
expect(result.meta.sortBy).toStrictEqual([['age', 'ASC']])
903+
expect(result.data.map((v) => v.id)).toStrictEqual(expectedResult)
904+
})
905+
879906
it('should return result based on sort and search on many-to-one relation', async () => {
880907
const config: PaginateConfig<CatToyEntity> = {
881908
relations: ['cat'],

src/paginate.ts

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import {
3434
mergeRelationSchema,
3535
Order,
3636
positiveNumberOrDefault,
37+
quoteVirtualColumn,
3738
RelationSchema,
3839
RelationSchemaInput,
3940
SortBy,
@@ -688,18 +689,18 @@ export async function paginate<T extends ObjectLiteral>(
688689
const isEmbedded = checkIsEmbedded(queryBuilder, columnProperties.propertyPath)
689690
let alias = fixColumnAlias(columnProperties, queryBuilder.alias, isRelation, isVirtualProperty, isEmbedded)
690691

692+
if (isVirtualProperty) {
693+
alias = quoteVirtualColumn(alias, isMySqlOrMariaDb)
694+
}
695+
691696
if (isMySqlOrMariaDb) {
692-
if (isVirtualProperty) {
693-
alias = `\`${alias}\``
694-
}
695697
if (nullSort) {
696-
queryBuilder.addOrderBy(`${alias} ${nullSort}`)
698+
const selectionAliasName = `${alias.replace(/\./g, '_')}IsNull`
699+
queryBuilder.addSelect(`${alias} ${nullSort}`, selectionAliasName)
700+
queryBuilder.addOrderBy(selectionAliasName)
697701
}
698702
queryBuilder.addOrderBy(alias, order[1])
699703
} else {
700-
if (isVirtualProperty) {
701-
alias = `"${alias}"`
702-
}
703704
queryBuilder.addOrderBy(alias, order[1], nullSort as 'NULLS FIRST' | 'NULLS LAST' | undefined)
704705
}
705706
}

0 commit comments

Comments
 (0)