Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(knex): allow changing FROM clause using QueryBuilder #3378

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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
derevnjuk marked this conversation as resolved.
Show resolved Hide resolved

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
@@ -1,17 +1,16 @@
import type { Knex } from 'knex';
import type {
AnyEntity, Collection, ConnectionType, Configuration, Constructor, CountOptions, DeleteOptions, Dictionary,
DriverMethodOptions, EntityData, EntityDictionary, EntityField, EntityManager, EntityMetadata, EntityProperty, FilterQuery,
DriverMethodOptions, EntityData, EntityDictionary, EntityField, EntityManager, EntityMetadata, EntityName, EntityProperty, FilterQuery,
FindOneOptions, FindOptions, IDatabaseDriver, LockOptions, NativeInsertUpdateManyOptions, NativeInsertUpdateOptions,
PopulateOptions, Primary, QueryOrderMap, QueryResult, RequiredEntityData, Transaction,
} 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
5 changes: 2 additions & 3 deletions packages/knex/src/SqlEntityManager.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { Knex } from 'knex';
import type { AnyEntity, ConnectionType, Dictionary, EntityData, EntityName, EntityRepository, GetRepository, QueryResult } from '@mikro-orm/core';
import { EntityManager, Utils } from '@mikro-orm/core';
import { EntityManager } from '@mikro-orm/core';
import type { AbstractSqlDriver } from './AbstractSqlDriver';
import { QueryBuilder } from './query';
import type { SqlEntityRepository } from './SqlEntityRepository';
Expand All @@ -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
9 changes: 9 additions & 0 deletions packages/knex/src/query/Alias.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import type { Knex } from 'knex';
import type { EntityMetadata } from '@mikro-orm/core';

export interface Alias {
aliasName: string;
entityName: string;
metadata?: EntityMetadata;
subQuery?: Knex.QueryBuilder;
}