From 6adb31eb3b8d77e4796fca09d4b0db7a39c400e8 Mon Sep 17 00:00:00 2001 From: Romain Lanz Date: Sat, 9 Mar 2024 10:30:43 +0100 Subject: [PATCH 1/4] feat(query_builder): add orderByRandom --- src/database/query_builder/database.ts | 21 +++++++++++++ src/types/querybuilder.ts | 1 + test/database/query_builder.spec.ts | 43 ++++++++++++++++++++++++++ 3 files changed, 65 insertions(+) diff --git a/src/database/query_builder/database.ts b/src/database/query_builder/database.ts index 926712b5..3e4a567d 100644 --- a/src/database/query_builder/database.ts +++ b/src/database/query_builder/database.ts @@ -281,6 +281,27 @@ export class DatabaseQueryBuilder extends Chainable implements DatabaseQueryBuil return this } + /** + * Order results by random value. + */ + orderByRandom(seed = '') { + switch (this.client.dialect.name) { + case 'sqlite3': + case 'better-sqlite3': + case 'postgres': + case 'redshift': + return this.orderByRaw('RANDOM()') + case 'mysql': + return this.orderByRaw(`RAND(${seed})`) + case 'mssql': + return this.orderByRaw('NEWID()') + case 'oracledb': + return this.orderByRaw('dbms_random.value') + default: + throw new Error(`Cannot order by random for the given dialect ${this.client.dialect.name}`) + } + } + /** * Executes the query */ diff --git a/src/types/querybuilder.ts b/src/types/querybuilder.ts index dd23c2cb..04cf1d5e 100644 --- a/src/types/querybuilder.ts +++ b/src/types/querybuilder.ts @@ -651,6 +651,7 @@ export interface ChainableContract { orderBy: OrderBy orderByRaw: RawQueryFn + orderByRandom: (seed?: string) => this union: Union unionAll: UnionAll diff --git a/test/database/query_builder.spec.ts b/test/database/query_builder.spec.ts index 4d6d5d40..3c4b1eea 100644 --- a/test/database/query_builder.spec.ts +++ b/test/database/query_builder.spec.ts @@ -5439,6 +5439,49 @@ test.group('Query Builder | orderByRaw', (group) => { }) }) +test.group('Query Builder | orderByRandom', (group) => { + group.setup(async () => { + await setup() + }) + + group.teardown(async () => { + await cleanup() + }) + + group.each.teardown(async () => { + await resetTables() + }) + + test('define order by random value', async ({ assert }) => { + const connection = new Connection('primary', getConfig(), logger) + connection.connect() + + const db = getQueryBuilder(getQueryClient(connection)) + await getInsertBuilder(getQueryClient(connection)) + .table('users') + .multiInsert([ + { + username: 'virk', + email: 'virk@adonisjs.com', + }, + { + username: 'romain', + email: 'romain@adonisjs.com', + }, + { + username: 'nikk', + email: 'nikk@adonisjs.com', + }, + ]) + + const users = await db.from('users').orderByRandom() + const users2 = await db.from('users').orderByRandom() + + assert.notEqual(users[0].id, users2[0].id) + await connection.disconnect() + }).retry(3) +}) + test.group('Query Builder | offset', (group) => { group.setup(async () => { await setup() From 68a724e0582f02abab7408893b64bb06229b4ffb Mon Sep 17 00:00:00 2001 From: Romain Lanz Date: Thu, 14 Mar 2024 18:32:01 +0100 Subject: [PATCH 2/4] feat(model_query_builder): add orderByRandom --- src/orm/query_builder/index.ts | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/orm/query_builder/index.ts b/src/orm/query_builder/index.ts index e34dc9b3..df01ab59 100644 --- a/src/orm/query_builder/index.ts +++ b/src/orm/query_builder/index.ts @@ -644,6 +644,27 @@ export class ModelQueryBuilder return this.addWhereHas(relationName, 'not', operator, value) } + /** + * Order results by random value. + */ + orderByRandom(seed = '') { + switch (this.client.dialect.name) { + case 'sqlite3': + case 'better-sqlite3': + case 'postgres': + case 'redshift': + return this.orderByRaw('RANDOM()') + case 'mysql': + return this.orderByRaw(`RAND(${seed})`) + case 'mssql': + return this.orderByRaw('NEWID()') + case 'oracledb': + return this.orderByRaw('dbms_random.value') + default: + throw new Error(`Cannot order by random for the given dialect ${this.client.dialect.name}`) + } + } + /** * Define a relationship to be preloaded */ From d283d1614d8d5580f443b66cf16d25292db0f2ed Mon Sep 17 00:00:00 2001 From: Romain Lanz Date: Wed, 4 Sep 2024 09:11:11 +0200 Subject: [PATCH 3/4] feat(query_builder): move orderByRandom to chainable class --- src/database/query_builder/chainable.ts | 23 +++++++++++++++++++++++ src/database/query_builder/database.ts | 23 +---------------------- src/orm/query_builder/index.ts | 22 +--------------------- test/database/query_builder.spec.ts | 14 ++++++++++---- 4 files changed, 35 insertions(+), 47 deletions(-) diff --git a/src/database/query_builder/chainable.ts b/src/database/query_builder/chainable.ts index d7ec061b..0caa9bcf 100644 --- a/src/database/query_builder/chainable.ts +++ b/src/database/query_builder/chainable.ts @@ -16,6 +16,7 @@ import { isObject } from '../../utils/index.js' import { RawQueryBuilder } from './raw.js' import { RawBuilder } from '../static_builder/raw.js' import { ReferenceBuilder } from '../static_builder/reference.js' +import type { DialectContract } from '../../types/database.js' /** * The chainable query builder to construct SQL queries for selecting, updating and @@ -105,6 +106,7 @@ export abstract class Chainable extends Macroable implements ChainableContract { constructor( public knexQuery: Knex.QueryBuilder, private queryCallback: DBQueryCallback, + private dialect: DialectContract, public keysResolver?: (columnName: string) => string ) { super() @@ -1701,6 +1703,27 @@ export abstract class Chainable extends Macroable implements ChainableContract { return this } + /** + * Order results by random value. + */ + orderByRandom(seed = '') { + switch (this.dialect.name) { + case 'sqlite3': + case 'better-sqlite3': + case 'postgres': + case 'redshift': + return this.orderByRaw('RANDOM()') + case 'mysql': + return this.orderByRaw(`RAND(${seed})`) + case 'mssql': + return this.orderByRaw('NEWID()') + case 'oracledb': + return this.orderByRaw('dbms_random.value') + default: + throw new Error(`Cannot order by random for the given dialect ${this.dialect.name}`) + } + } + /** * Define select offset */ diff --git a/src/database/query_builder/database.ts b/src/database/query_builder/database.ts index 3e4a567d..32fe35a4 100644 --- a/src/database/query_builder/database.ts +++ b/src/database/query_builder/database.ts @@ -70,7 +70,7 @@ export class DatabaseQueryBuilder extends Chainable implements DatabaseQueryBuil public client: QueryClientContract, public keysResolver?: (columnName: string) => string ) { - super(builder, queryCallback, keysResolver) + super(builder, queryCallback, client.dialect, keysResolver) this.debugQueries = this.client.debug } @@ -281,27 +281,6 @@ export class DatabaseQueryBuilder extends Chainable implements DatabaseQueryBuil return this } - /** - * Order results by random value. - */ - orderByRandom(seed = '') { - switch (this.client.dialect.name) { - case 'sqlite3': - case 'better-sqlite3': - case 'postgres': - case 'redshift': - return this.orderByRaw('RANDOM()') - case 'mysql': - return this.orderByRaw(`RAND(${seed})`) - case 'mssql': - return this.orderByRaw('NEWID()') - case 'oracledb': - return this.orderByRaw('dbms_random.value') - default: - throw new Error(`Cannot order by random for the given dialect ${this.client.dialect.name}`) - } - } - /** * Executes the query */ diff --git a/src/orm/query_builder/index.ts b/src/orm/query_builder/index.ts index df01ab59..135fa5fa 100644 --- a/src/orm/query_builder/index.ts +++ b/src/orm/query_builder/index.ts @@ -150,6 +150,7 @@ export class ModelQueryBuilder super( builder, customFn, + client.dialect, model.$keys.attributesToColumns.resolve.bind(model.$keys.attributesToColumns) ) @@ -644,27 +645,6 @@ export class ModelQueryBuilder return this.addWhereHas(relationName, 'not', operator, value) } - /** - * Order results by random value. - */ - orderByRandom(seed = '') { - switch (this.client.dialect.name) { - case 'sqlite3': - case 'better-sqlite3': - case 'postgres': - case 'redshift': - return this.orderByRaw('RANDOM()') - case 'mysql': - return this.orderByRaw(`RAND(${seed})`) - case 'mssql': - return this.orderByRaw('NEWID()') - case 'oracledb': - return this.orderByRaw('dbms_random.value') - default: - throw new Error(`Cannot order by random for the given dialect ${this.client.dialect.name}`) - } - } - /** * Define a relationship to be preloaded */ diff --git a/test/database/query_builder.spec.ts b/test/database/query_builder.spec.ts index 3c4b1eea..12e43eec 100644 --- a/test/database/query_builder.spec.ts +++ b/test/database/query_builder.spec.ts @@ -5474,12 +5474,18 @@ test.group('Query Builder | orderByRandom', (group) => { }, ]) - const users = await db.from('users').orderByRandom() - const users2 = await db.from('users').orderByRandom() + const users = [] + + for (let i = 0; i < 10; i++) { + const result = await db.from('users').orderByRandom() + + users.push(result.map((user) => user.id)) + } + + // TODO: Check which assertion is better to use - assert.notEqual(users[0].id, users2[0].id) await connection.disconnect() - }).retry(3) + }) }) test.group('Query Builder | offset', (group) => { From e3d9c7eec1ca3fa37beff03b96579c931fba4cd2 Mon Sep 17 00:00:00 2001 From: Romain Lanz Date: Fri, 6 Sep 2024 08:16:13 +0200 Subject: [PATCH 4/4] test(query_builder): add proper assertion for orderByRandom --- test/database/query_builder.spec.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/database/query_builder.spec.ts b/test/database/query_builder.spec.ts index 12e43eec..9fbc9192 100644 --- a/test/database/query_builder.spec.ts +++ b/test/database/query_builder.spec.ts @@ -5474,15 +5474,15 @@ test.group('Query Builder | orderByRandom', (group) => { }, ]) - const users = [] + const userResults: number[][] = [] for (let i = 0; i < 10; i++) { const result = await db.from('users').orderByRandom() - users.push(result.map((user) => user.id)) + userResults.push(result.map((user) => user.id)) } - // TODO: Check which assertion is better to use + assert.isTrue(userResults.some((users) => userResults[0] !== users)) await connection.disconnect() })