Skip to content

Commit

Permalink
feat(knex): allow changing FROM clause using QueryBuilder
Browse files Browse the repository at this point in the history
closes #3374
  • Loading branch information
derevnjuk committed Sep 9, 2022
1 parent af8825e commit 7ed2d62
Show file tree
Hide file tree
Showing 8 changed files with 313 additions and 62 deletions.
34 changes: 34 additions & 0 deletions docs/docs/query-builder.md
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,40 @@ const count = await qb.getCount();

This will also remove any existing limit and offset from the query (the QB will be cloned under the hood, so calling `getCount()` does not mutate the original QB state).

## Overriding FROM clause

You can specify the table used in the `FROM` clause, replacing the current table name if one has already been specified. This is typically used to specify a sub-query expression in SQL.

```ts
const qb = em.createQueryBuilder(Book2);
qb.select('*').from(Author2).where({ id: { $gt: 2 } });

console.log(qb.getQuery());
// select `e0`.* from `author2` as `e0` where `e0`.`id` > 2;
```

You can also use sub-queries in the `FROM` like this:

```ts
const qb1 = em.createQueryBuilder(Book2).where({ id: { $lte: new Date() } }).orderBy({ id: 'DESC' }).limit(10);
const qb2 = em.createQueryBuilder(qb1.clone())
qb2.select('*').orderBy({ id: 'ASC' });

console.log(qb2.getQuery());
// select `e1`.* from (select `e0`.* from `book2` as `e0` where `e0`.`id` <= ? order by `e0`.`id` desc limit ?) as `e1` order by `e1`.`id`;
```

To set up an alias to refer to a table in a `SELECT` statement, pass the second argument as follows:

```ts
const qb1 = em.createQueryBuilder(Book2, 'b1').where({ id: { $lte: new Date() } }).orderBy({ id: 'DESC' }).limit(10);
const qb2 = em.createQueryBuilder(qb1.clone(), 'b2')
qb2.select('*').orderBy({ id: 'ASC' });

console.log(qb2.getQuery());
// select `b2`.* from (select `b1`.* from `book2` as `b1` where `b1`.`id` <= ? order by `b1`.`id` desc limit ?) as `b2` order by `b2`.`id`;
```

## Using sub-queries

You can filter using sub-queries in where conditions:
Expand Down
34 changes: 34 additions & 0 deletions docs/versioned_docs/version-5.3/query-builder.md
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,40 @@ const count = await qb.getCount();

This will also remove any existing limit and offset from the query (the QB will be cloned under the hood, so calling `getCount()` does not mutate the original QB state).

## Overriding FROM clause

You can specify the table used in the `FROM` clause, replacing the current table name if one has already been specified. This is typically used to specify a sub-query expression in SQL.

```ts
const qb = em.createQueryBuilder(Book2);
qb.select('*').from(Author2).where({ id: { $gt: 2 } });

console.log(qb.getQuery());
// select `e0`.* from `author2` as `e0` where `e0`.`id` > 2;
```

You can also use sub-queries in the `FROM` like this:

```ts
const qb1 = em.createQueryBuilder(Book2).where({ id: { $lte: new Date() } }).orderBy({ id: 'DESC' }).limit(10);
const qb2 = em.createQueryBuilder(qb1.clone())
qb2.select('*').orderBy({ id: 'ASC' });

console.log(qb2.getQuery());
// select `e1`.* from (select `e0`.* from `book2` as `e0` where `e0`.`id` <= ? order by `e0`.`id` desc limit ?) as `e1` order by `e1`.`id`;
```

To set up an alias to refer to a table in a `SELECT` statement, pass the second argument as follows:

```ts
const qb1 = em.createQueryBuilder(Book2, 'b1').where({ id: { $lte: new Date() } }).orderBy({ id: 'DESC' }).limit(10);
const qb2 = em.createQueryBuilder(qb1.clone(), 'b2')
qb2.select('*').orderBy({ id: 'ASC' });

console.log(qb2.getQuery());
// select `b2`.* from (select `b1`.* from `book2` as `b1` where `b1`.`id` <= ? order by `b1`.`id` desc limit ?) as `b2` order by `b2`.`id`;
```

## Using sub-queries

You can filter using sub-queries in where conditions:
Expand Down
7 changes: 3 additions & 4 deletions packages/knex/src/AbstractSqlDriver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,13 @@ import type {
DriverMethodOptions, EntityData, EntityDictionary, EntityField, EntityManager, EntityMetadata, EntityProperty, FilterQuery,
FindOneOptions, FindOptions, IDatabaseDriver, LockOptions, NativeInsertUpdateManyOptions, NativeInsertUpdateOptions,
PopulateOptions, Primary, QueryOrderMap, QueryResult, RequiredEntityData, Transaction,
} from '@mikro-orm/core';
EntityName } from '@mikro-orm/core';
import { DatabaseDriver, EntityManagerType, helper, LoadStrategy, QueryFlag, QueryHelper, ReferenceType, Utils } from '@mikro-orm/core';
import type { AbstractSqlConnection } from './AbstractSqlConnection';
import type { AbstractSqlPlatform } from './AbstractSqlPlatform';
import { QueryBuilder } from './query/QueryBuilder';
import { QueryBuilder, QueryType } from './query';
import { SqlEntityManager } from './SqlEntityManager';
import type { Field } from './typings';
import { QueryType } from './query';

export abstract class AbstractSqlDriver<C extends AbstractSqlConnection = AbstractSqlConnection> extends DatabaseDriver<C> {

Expand Down Expand Up @@ -712,7 +711,7 @@ export abstract class AbstractSqlDriver<C extends AbstractSqlConnection = Abstra
}

/** @internal */
createQueryBuilder<T extends object>(entityName: string, ctx?: Transaction<Knex.Transaction>, preferredConnectionType?: ConnectionType, convertCustomTypes?: boolean): QueryBuilder<T> {
createQueryBuilder<T extends object>(entityName: EntityName<T> | QueryBuilder<T>, ctx?: Transaction<Knex.Transaction>, preferredConnectionType?: ConnectionType, convertCustomTypes?: boolean): QueryBuilder<T> {
const connectionType = this.resolveConnectionType({ ctx, connectionType: preferredConnectionType });
const qb = new QueryBuilder<T>(entityName, this.metadata, this, ctx, undefined, connectionType);

Expand Down
3 changes: 1 addition & 2 deletions packages/knex/src/SqlEntityManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,7 @@ export class SqlEntityManager<D extends AbstractSqlDriver = AbstractSqlDriver> e
/**
* Creates a QueryBuilder instance
*/
createQueryBuilder<T extends object>(entityName: EntityName<T>, alias?: string, type?: ConnectionType): QueryBuilder<T> {
entityName = Utils.className(entityName);
createQueryBuilder<T extends object>(entityName: EntityName<T> | QueryBuilder<T>, alias?: string, type?: ConnectionType): QueryBuilder<T> {
const context = this.getContext();

return new QueryBuilder<T>(entityName, this.getMetadata(), this.getDriver(), context.getTransactionContext(), alias, type, context);
Expand Down
15 changes: 15 additions & 0 deletions packages/knex/src/query/Alias.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import type { Knex } from 'knex';
import type { EntityMetadata } from '@mikro-orm/core';

export class Alias {

aliasName!: string;
entityName!: string;
metadata?: EntityMetadata;
subQuery?: Knex.QueryBuilder;

constructor(params: Alias) {
Object.assign(this, params);
}

}
Loading

0 comments on commit 7ed2d62

Please sign in to comment.