diff --git a/packages/knex/src/AbstractSqlPlatform.ts b/packages/knex/src/AbstractSqlPlatform.ts index c4d93eb33c3e..0090a3eea5f6 100644 --- a/packages/knex/src/AbstractSqlPlatform.ts +++ b/packages/knex/src/AbstractSqlPlatform.ts @@ -135,4 +135,11 @@ export abstract class AbstractSqlPlatform extends Platform { return ret + 'else null end)'; } + /** + * @internal + */ + getOrderByExpression(column: string, direction: string): string[] { + return [ `${column} ${direction.toLowerCase()}` ]; + } + } diff --git a/packages/knex/src/query/QueryBuilderHelper.ts b/packages/knex/src/query/QueryBuilderHelper.ts index b94692feae25..e87c764c3b9e 100644 --- a/packages/knex/src/query/QueryBuilderHelper.ts +++ b/packages/knex/src/query/QueryBuilderHelper.ts @@ -638,7 +638,7 @@ export class QueryBuilderHelper { if (Array.isArray(order)) { order.forEach(part => ret.push(...this.getQueryOrderFromObject(type, part, populate))); } else { - ret.push(`${colPart} ${order.toLowerCase()}`); + ret.push(...this.platform.getOrderByExpression(colPart, order)); } } } diff --git a/packages/mariadb/src/MariaDbPlatform.ts b/packages/mariadb/src/MariaDbPlatform.ts index 9b4b370c9fb0..3f95f97916ad 100644 --- a/packages/mariadb/src/MariaDbPlatform.ts +++ b/packages/mariadb/src/MariaDbPlatform.ts @@ -1,4 +1,4 @@ -import { AbstractSqlPlatform } from '@mikro-orm/knex'; +import { AbstractSqlPlatform, QueryOrder } from '@mikro-orm/knex'; import { MariaDbSchemaHelper } from './MariaDbSchemaHelper'; import { MariaDbExceptionConverter } from './MariaDbExceptionConverter'; import { Utils, type SimpleColumnMeta, type Dictionary, type Type } from '@mikro-orm/core'; @@ -74,4 +74,25 @@ export class MariaDbPlatform extends AbstractSqlPlatform { return `alter table ${quotedTableName} add fulltext index ${quotedIndexName}(${quotedColumnNames.join(',')})`; } + private readonly ORDER_BY_NULLS_TRANSLATE = { + [QueryOrder.asc_nulls_first]: 'is not null', + [QueryOrder.asc_nulls_last]: 'is null', + [QueryOrder.desc_nulls_first]: 'is not null', + [QueryOrder.desc_nulls_last]: 'is null', + } as const; + + /* istanbul ignore next */ + override getOrderByExpression(column: string, direction: string): string[] { + const ret: string[] = []; + const dir = direction.toLowerCase() as keyof typeof this.ORDER_BY_NULLS_TRANSLATE; + + if (dir in this.ORDER_BY_NULLS_TRANSLATE) { + ret.push(`${column} ${this.ORDER_BY_NULLS_TRANSLATE[dir]}`); + } + + ret.push(`${column} ${dir.replace(/(\s|nulls|first|last)*/gi, '')}`); + + return ret; + } + } diff --git a/packages/mysql/src/MySqlPlatform.ts b/packages/mysql/src/MySqlPlatform.ts index 73cfeed470d6..d8bb92cbfef9 100644 --- a/packages/mysql/src/MySqlPlatform.ts +++ b/packages/mysql/src/MySqlPlatform.ts @@ -1,4 +1,8 @@ -import { AbstractSqlPlatform, type IndexDef } from '@mikro-orm/knex'; +import { + AbstractSqlPlatform, + type IndexDef, + QueryOrder, +} from '@mikro-orm/knex'; import { MySqlSchemaHelper } from './MySqlSchemaHelper'; import { MySqlExceptionConverter } from './MySqlExceptionConverter'; import { Utils, type SimpleColumnMeta, type Dictionary, type Type, type TransformContext } from '@mikro-orm/core'; @@ -88,4 +92,24 @@ export class MySqlPlatform extends AbstractSqlPlatform { return `alter table ${quotedTableName} add fulltext index ${quotedIndexName}(${quotedColumnNames.join(',')})`; } + private readonly ORDER_BY_NULLS_TRANSLATE = { + [QueryOrder.asc_nulls_first]: 'is not null', + [QueryOrder.asc_nulls_last]: 'is null', + [QueryOrder.desc_nulls_first]: 'is not null', + [QueryOrder.desc_nulls_last]: 'is null', + } as const; + + override getOrderByExpression(column: string, direction: string): string[] { + const ret: string[] = []; + const dir = direction.toLowerCase() as keyof typeof this.ORDER_BY_NULLS_TRANSLATE; + + if (dir in this.ORDER_BY_NULLS_TRANSLATE) { + ret.push(`${column} ${this.ORDER_BY_NULLS_TRANSLATE[dir]}`); + } + + ret.push(`${column} ${dir.replace(/(\s|nulls|first|last)*/gi, '')}`); + + return ret; + } + } diff --git a/tests/QueryBuilder.test.ts b/tests/QueryBuilder.test.ts index cb62453a3ba2..07ba6996076d 100644 --- a/tests/QueryBuilder.test.ts +++ b/tests/QueryBuilder.test.ts @@ -2207,13 +2207,13 @@ describe('QueryBuilder', () => { test('order by asc nulls first', async () => { const qb = orm.em.createQueryBuilder(Publisher2); qb.select('*').orderBy({ name: QueryOrder.ASC_NULLS_FIRST }); - expect(qb.getQuery()).toEqual('select `e0`.* from `publisher2` as `e0` order by `e0`.`name` asc nulls first'); + expect(qb.getQuery()).toEqual('select `e0`.* from `publisher2` as `e0` order by `e0`.`name` is not null, `e0`.`name` asc'); }); test('order by nulls last', async () => { const qb = orm.em.createQueryBuilder(Publisher2); qb.select('*').orderBy({ name: QueryOrder.DESC_NULLS_LAST, type: QueryOrder.ASC_NULLS_LAST }); - expect(qb.getQuery()).toEqual('select `e0`.* from `publisher2` as `e0` order by `e0`.`name` desc nulls last, `e0`.`type` asc nulls last'); + expect(qb.getQuery()).toEqual('select `e0`.* from `publisher2` as `e0` order by `e0`.`name` is null, `e0`.`name` desc, `e0`.`type` is null, `e0`.`type` asc'); }); test('order by custom expression', async () => {