kysely
Version:
Type safe SQL query builder
221 lines (220 loc) • 7.05 kB
JavaScript
/// <reference types="./aggregate-function-builder.d.ts" />
import { freeze } from '../util/object-utils.js';
import { AggregateFunctionNode } from '../operation-node/aggregate-function-node.js';
import { AliasNode } from '../operation-node/alias-node.js';
import { IdentifierNode } from '../operation-node/identifier-node.js';
import { preventAwait } from '../util/prevent-await.js';
import { createOverBuilder } from '../parser/parse-utils.js';
import { parseReferentialBinaryOperation, parseValueBinaryOperationOrExpression, } from '../parser/binary-operation-parser.js';
export class AggregateFunctionBuilder {
#props;
constructor(props) {
this.#props = freeze(props);
}
/** @private */
get expressionType() {
return undefined;
}
/**
* Returns an aliased version of the function.
*
* In addition to slapping `as "the_alias"` to the end of the SQL,
* this method also provides strict typing:
*
* ```ts
* const result = await db
* .selectFrom('person')
* .select(
* (eb) => eb.fn.count<number>('id').as('person_count')
* )
* .executeTakeFirstOrThrow()
*
* // `person_count: number` field exists in the result type.
* console.log(result.person_count)
* ```
*
* The generated SQL (PostgreSQL):
*
* ```sql
* select count("id") as "person_count"
* from "person"
* ```
*/
as(alias) {
return new AliasedAggregateFunctionBuilder(this, alias);
}
/**
* Adds a `distinct` clause inside the function.
*
* ### Examples
*
* ```ts
* const result = await db
* .selectFrom('person')
* .select((eb) =>
* eb.fn.count<number>('first_name').distinct().as('first_name_count')
* )
* .executeTakeFirstOrThrow()
* ```
*
* The generated SQL (PostgreSQL):
*
* ```sql
* select count(distinct "first_name") as "first_name_count"
* from "person"
* ```
*/
distinct() {
return new AggregateFunctionBuilder({
...this.#props,
aggregateFunctionNode: AggregateFunctionNode.cloneWithDistinct(this.#props.aggregateFunctionNode),
});
}
filterWhere(...args) {
return new AggregateFunctionBuilder({
...this.#props,
aggregateFunctionNode: AggregateFunctionNode.cloneWithFilter(this.#props.aggregateFunctionNode, parseValueBinaryOperationOrExpression(args)),
});
}
/**
* Adds a `filter` clause with a nested `where` clause after the function, where
* both sides of the operator are references to columns.
*
* Similar to {@link WhereInterface}'s `whereRef` method.
*
* ### Examples
*
* Count people with same first and last names versus general public:
*
* ```ts
* const result = await db
* .selectFrom('person')
* .select((eb) => [
* eb.fn
* .count<number>('id')
* .filterWhereRef('first_name', '=', 'last_name')
* .as('repeat_name_count'),
* eb.fn.count<number>('id').as('total_count'),
* ])
* .executeTakeFirstOrThrow()
* ```
*
* The generated SQL (PostgreSQL):
*
* ```sql
* select
* count("id") filter(where "first_name" = "last_name") as "repeat_name_count",
* count("id") as "total_count"
* from "person"
* ```
*/
filterWhereRef(lhs, op, rhs) {
return new AggregateFunctionBuilder({
...this.#props,
aggregateFunctionNode: AggregateFunctionNode.cloneWithFilter(this.#props.aggregateFunctionNode, parseReferentialBinaryOperation(lhs, op, rhs)),
});
}
/**
* Adds an `over` clause (window functions) after the function.
*
* ### Examples
*
* ```ts
* const result = await db
* .selectFrom('person')
* .select(
* (eb) => eb.fn.avg<number>('age').over().as('average_age')
* )
* .execute()
* ```
*
* The generated SQL (PostgreSQL):
*
* ```sql
* select avg("age") over() as "average_age"
* from "person"
* ```
*
* Also supports passing a callback that returns an over builder,
* allowing to add partition by and sort by clauses inside over.
*
* ```ts
* const result = await db
* .selectFrom('person')
* .select(
* (eb) => eb.fn.avg<number>('age').over(
* ob => ob.partitionBy('last_name').orderBy('first_name', 'asc')
* ).as('average_age')
* )
* .execute()
* ```
*
* The generated SQL (PostgreSQL):
*
* ```sql
* select avg("age") over(partition by "last_name" order by "first_name" asc) as "average_age"
* from "person"
* ```
*/
over(over) {
const builder = createOverBuilder();
return new AggregateFunctionBuilder({
...this.#props,
aggregateFunctionNode: AggregateFunctionNode.cloneWithOver(this.#props.aggregateFunctionNode, (over ? over(builder) : builder).toOperationNode()),
});
}
/**
* Simply calls the provided function passing `this` as the only argument. `$call` returns
* what the provided function returns.
*/
$call(func) {
return func(this);
}
/**
* Casts the expression to the given type.
*
* This method call doesn't change the SQL in any way. This methods simply
* returns a copy of this `AggregateFunctionBuilder` with a new output type.
*/
$castTo() {
return new AggregateFunctionBuilder(this.#props);
}
/**
* Omit null from the expression's type.
*
* This function can be useful in cases where you know an expression can't be
* null, but Kysely is unable to infer it.
*
* This method call doesn't change the SQL in any way. This methods simply
* returns a copy of `this` with a new output type.
*/
$notNull() {
return new AggregateFunctionBuilder(this.#props);
}
toOperationNode() {
return this.#props.aggregateFunctionNode;
}
}
preventAwait(AggregateFunctionBuilder, "don't await AggregateFunctionBuilder instances. They are never executed directly and are always just a part of a query.");
/**
* {@link AggregateFunctionBuilder} with an alias. The result of calling {@link AggregateFunctionBuilder.as}.
*/
export class AliasedAggregateFunctionBuilder {
#aggregateFunctionBuilder;
#alias;
constructor(aggregateFunctionBuilder, alias) {
this.#aggregateFunctionBuilder = aggregateFunctionBuilder;
this.#alias = alias;
}
/** @private */
get expression() {
return this.#aggregateFunctionBuilder;
}
/** @private */
get alias() {
return this.#alias;
}
toOperationNode() {
return AliasNode.create(this.#aggregateFunctionBuilder.toOperationNode(), IdentifierNode.create(this.#alias));
}
}