Skip to content
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,70 @@ import { Result } from '@expo/results';
import {
FieldEqualityCondition,
isSingleValueFieldEqualityCondition,
QuerySelectionModifiers,
QuerySelectionModifiersWithOrderByFragment,
QuerySelectionModifiersWithOrderByRaw,
OrderByOrdering,
} from './BasePostgresEntityDatabaseAdapter';
import { BaseSQLQueryBuilder } from './BaseSQLQueryBuilder';
import { SQLFragment } from './SQLOperator';
import { EntityKnexDataManager } from './internal/EntityKnexDataManager';

export interface EntityLoaderOrderByClause<
TFields extends Record<string, any>,
TSelectedFields extends keyof TFields,
> {
/**
* The field name to order by.
*/
fieldName: TSelectedFields;

/**
* The OrderByOrdering to order by.
*/
order: OrderByOrdering;
}

/**
* SQL modifiers that only affect the selection but not the projection.
*/
export interface EntityLoaderQuerySelectionModifiers<
TFields extends Record<string, any>,
TSelectedFields extends keyof TFields,
> {
/**
* Order the entities by specified columns and orders.
*/
orderBy?: readonly EntityLoaderOrderByClause<TFields, TSelectedFields>[];

/**
* Skip the specified number of entities queried before returning.
*/
offset?: number;

/**
* Limit the number of entities returned.
*/
limit?: number;
}

export interface EntityLoaderQuerySelectionModifiersWithOrderByRaw<
TFields extends Record<string, any>,
TSelectedFields extends keyof TFields,
> extends EntityLoaderQuerySelectionModifiers<TFields, TSelectedFields> {
/**
* Order the entities by a raw SQL `ORDER BY` clause.
*/
orderByRaw?: string;
}

export interface EntityLoaderQuerySelectionModifiersWithOrderByFragment<
TFields extends Record<string, any>,
TSelectedFields extends keyof TFields,
> extends EntityLoaderQuerySelectionModifiers<TFields, TSelectedFields> {
/**
* Order the entities by a SQL fragment `ORDER BY` clause.
*/
orderByFragment?: SQLFragment;
}

/**
* Authorization-result-based knex entity loader for non-data-loader-based load methods.
* All loads through this loader are results (or null for some loader methods), where an
Expand Down Expand Up @@ -60,8 +116,11 @@ export class AuthorizationResultBasedKnexEntityLoader<
*/
async loadFirstByFieldEqualityConjunctionAsync<N extends keyof Pick<TFields, TSelectedFields>>(
fieldEqualityOperands: FieldEqualityCondition<TFields, N>[],
querySelectionModifiers: Omit<QuerySelectionModifiers<TFields>, 'limit'> &
Required<Pick<QuerySelectionModifiers<TFields>, 'orderBy'>>,
querySelectionModifiers: Omit<
EntityLoaderQuerySelectionModifiers<TFields, TSelectedFields>,
'limit'
> &
Required<Pick<EntityLoaderQuerySelectionModifiers<TFields, TSelectedFields>, 'orderBy'>>,
): Promise<Result<TEntity> | null> {
const results = await this.loadManyByFieldEqualityConjunctionAsync(fieldEqualityOperands, {
...querySelectionModifiers,
Expand All @@ -76,7 +135,7 @@ export class AuthorizationResultBasedKnexEntityLoader<
*/
async loadManyByFieldEqualityConjunctionAsync<N extends keyof Pick<TFields, TSelectedFields>>(
fieldEqualityOperands: FieldEqualityCondition<TFields, N>[],
querySelectionModifiers: QuerySelectionModifiers<TFields> = {},
querySelectionModifiers: EntityLoaderQuerySelectionModifiers<TFields, TSelectedFields> = {},
): Promise<readonly Result<TEntity>[]> {
for (const fieldEqualityOperand of fieldEqualityOperands) {
const fieldValues = isSingleValueFieldEqualityCondition(fieldEqualityOperand)
Expand All @@ -101,7 +160,10 @@ export class AuthorizationResultBasedKnexEntityLoader<
async loadManyByRawWhereClauseAsync(
rawWhereClause: string,
bindings: any[] | object,
querySelectionModifiers: QuerySelectionModifiersWithOrderByRaw<TFields> = {},
querySelectionModifiers: EntityLoaderQuerySelectionModifiersWithOrderByRaw<
TFields,
TSelectedFields
> = {},
): Promise<readonly Result<TEntity>[]> {
const fieldObjects = await this.knexDataManager.loadManyByRawWhereClauseAsync(
this.queryContext,
Expand All @@ -118,7 +180,10 @@ export class AuthorizationResultBasedKnexEntityLoader<
*/
loadManyBySQL(
fragment: SQLFragment,
modifiers: QuerySelectionModifiersWithOrderByFragment<TFields> = {},
modifiers: EntityLoaderQuerySelectionModifiersWithOrderByFragment<
TFields,
TSelectedFields
> = {},
): AuthorizationResultBasedSQLQueryBuilder<
TFields,
TIDField,
Expand Down Expand Up @@ -153,7 +218,7 @@ export class AuthorizationResultBasedSQLQueryBuilder<
TSelectedFields
>,
TSelectedFields extends keyof TFields,
> extends BaseSQLQueryBuilder<TFields, Result<TEntity>> {
> extends BaseSQLQueryBuilder<TFields, TSelectedFields, Result<TEntity>> {
constructor(
private readonly knexDataManager: EntityKnexDataManager<TFields, TIDField>,
private readonly constructionUtils: EntityConstructionUtils<
Expand All @@ -166,7 +231,7 @@ export class AuthorizationResultBasedSQLQueryBuilder<
>,
private readonly queryContext: EntityQueryContext,
sqlFragment: SQLFragment,
modifiers: QuerySelectionModifiersWithOrderByFragment<TFields>,
modifiers: EntityLoaderQuerySelectionModifiersWithOrderByFragment<TFields, TSelectedFields>,
) {
super(sqlFragment, modifiers);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,24 +75,26 @@ export enum OrderByOrdering {
DESCENDING = 'desc',
}

export interface PostgresOrderByClause<TFields extends Record<string, any>> {
/**
* The field name to order by.
*/
fieldName: keyof TFields;

/**
* The OrderByOrdering to order by.
*/
order: OrderByOrdering;
}

/**
* SQL modifiers that only affect the selection but not the projection.
*/
export interface QuerySelectionModifiers<TFields extends Record<string, any>> {
export interface PostgresQuerySelectionModifiers<TFields extends Record<string, any>> {
/**
* Order the entities by specified columns and orders.
*/
orderBy?: {
/**
* The field name to order by.
*/
fieldName: keyof TFields;

/**
* The OrderByOrdering to order by.
*/
order: OrderByOrdering;
}[];
orderBy?: readonly PostgresOrderByClause<TFields>[];

/**
* Skip the specified number of entities queried before returning.
Expand All @@ -105,18 +107,18 @@ export interface QuerySelectionModifiers<TFields extends Record<string, any>> {
limit?: number;
}

export interface QuerySelectionModifiersWithOrderByRaw<
export interface PostgresQuerySelectionModifiersWithOrderByRaw<
TFields extends Record<string, any>,
> extends QuerySelectionModifiers<TFields> {
> extends PostgresQuerySelectionModifiers<TFields> {
/**
* Order the entities by a raw SQL `ORDER BY` clause.
*/
orderByRaw?: string;
}

export interface QuerySelectionModifiersWithOrderByFragment<
export interface PostgresQuerySelectionModifiersWithOrderByFragment<
TFields extends Record<string, any>,
> extends QuerySelectionModifiers<TFields> {
> extends PostgresQuerySelectionModifiers<TFields> {
/**
* Order the entities by a SQL fragment `ORDER BY` clause.
*/
Expand Down Expand Up @@ -159,7 +161,7 @@ export abstract class BasePostgresEntityDatabaseAdapter<
async fetchManyByFieldEqualityConjunctionAsync<N extends keyof TFields>(
queryContext: EntityQueryContext,
fieldEqualityOperands: FieldEqualityCondition<TFields, N>[],
querySelectionModifiers: QuerySelectionModifiers<TFields>,
querySelectionModifiers: PostgresQuerySelectionModifiers<TFields>,
): Promise<readonly Readonly<TFields>[]> {
const tableFieldSingleValueOperands: TableFieldSingleValueEqualityCondition[] = [];
const tableFieldMultipleValueOperands: TableFieldMultiValueEqualityCondition[] = [];
Expand Down Expand Up @@ -211,7 +213,7 @@ export abstract class BasePostgresEntityDatabaseAdapter<
queryContext: EntityQueryContext,
rawWhereClause: string,
bindings: any[] | object,
querySelectionModifiers: QuerySelectionModifiersWithOrderByRaw<TFields>,
querySelectionModifiers: PostgresQuerySelectionModifiersWithOrderByRaw<TFields>,
): Promise<readonly Readonly<TFields>[]> {
const results = await this.fetchManyByRawWhereClauseInternalAsync(
queryContext.getQueryInterface(),
Expand Down Expand Up @@ -245,7 +247,7 @@ export abstract class BasePostgresEntityDatabaseAdapter<
async fetchManyBySQLFragmentAsync(
queryContext: EntityQueryContext,
sqlFragment: SQLFragment,
querySelectionModifiers: QuerySelectionModifiersWithOrderByFragment<TFields>,
querySelectionModifiers: PostgresQuerySelectionModifiersWithOrderByFragment<TFields>,
): Promise<readonly Readonly<TFields>[]> {
const results = await this.fetchManyBySQLFragmentInternalAsync(
queryContext.getQueryInterface(),
Expand All @@ -267,7 +269,7 @@ export abstract class BasePostgresEntityDatabaseAdapter<
): Promise<object[]>;

private convertToTableQueryModifiersWithOrderByRaw(
querySelectionModifiers: QuerySelectionModifiersWithOrderByRaw<TFields>,
querySelectionModifiers: PostgresQuerySelectionModifiersWithOrderByRaw<TFields>,
): TableQuerySelectionModifiersWithOrderByRaw {
return {
...this.convertToTableQueryModifiers(querySelectionModifiers),
Expand All @@ -276,7 +278,7 @@ export abstract class BasePostgresEntityDatabaseAdapter<
}

private convertToTableQueryModifiersWithOrderByFragment(
querySelectionModifiers: QuerySelectionModifiersWithOrderByFragment<TFields>,
querySelectionModifiers: PostgresQuerySelectionModifiersWithOrderByFragment<TFields>,
): TableQuerySelectionModifiersWithOrderByFragment {
return {
...this.convertToTableQueryModifiers(querySelectionModifiers),
Expand All @@ -285,7 +287,7 @@ export abstract class BasePostgresEntityDatabaseAdapter<
}

private convertToTableQueryModifiers(
querySelectionModifiers: QuerySelectionModifiers<TFields>,
querySelectionModifiers: PostgresQuerySelectionModifiers<TFields>,
): TableQuerySelectionModifiers {
const orderBy = querySelectionModifiers.orderBy;
return {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,26 @@
import {
OrderByOrdering,
QuerySelectionModifiersWithOrderByFragment,
} from './BasePostgresEntityDatabaseAdapter';
EntityLoaderOrderByClause,
EntityLoaderQuerySelectionModifiersWithOrderByFragment,
} from './AuthorizationResultBasedKnexEntityLoader';
import { OrderByOrdering } from './BasePostgresEntityDatabaseAdapter';
import { SQLFragment } from './SQLOperator';

/**
* Base SQL query builder that provides common functionality for building SQL queries.
*/
export abstract class BaseSQLQueryBuilder<TFields extends Record<string, any>, TResultType> {
export abstract class BaseSQLQueryBuilder<
TFields extends Record<string, any>,
TSelectedFields extends keyof TFields,
TResultType,
> {
private executed = false;

constructor(
private readonly sqlFragment: SQLFragment,
private readonly modifiers: {
limit?: number;
offset?: number;
orderBy?: { fieldName: keyof TFields; order: OrderByOrdering }[];
orderBy?: readonly EntityLoaderOrderByClause<TFields, TSelectedFields>[];
orderByFragment?: SQLFragment;
},
) {}
Expand All @@ -39,7 +44,7 @@ export abstract class BaseSQLQueryBuilder<TFields extends Record<string, any>, T
/**
* Order by a field. Can be called multiple times to add multiple order bys.
*/
orderBy(fieldName: keyof TFields, order: OrderByOrdering = OrderByOrdering.ASCENDING): this {
orderBy(fieldName: TSelectedFields, order: OrderByOrdering = OrderByOrdering.ASCENDING): this {
this.modifiers.orderBy = [...(this.modifiers.orderBy ?? []), { fieldName, order }];
return this;
}
Expand Down Expand Up @@ -71,7 +76,10 @@ export abstract class BaseSQLQueryBuilder<TFields extends Record<string, any>, T
/**
* Get the current modifiers as QuerySelectionModifiersWithOrderByFragment<TFields>
*/
protected getModifiers(): QuerySelectionModifiersWithOrderByFragment<TFields> {
protected getModifiers(): EntityLoaderQuerySelectionModifiersWithOrderByFragment<
TFields,
TSelectedFields
> {
return this.modifiers;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { EntityPrivacyPolicy, ReadonlyEntity, ViewerContext } from '@expo/entity';

import { AuthorizationResultBasedKnexEntityLoader } from './AuthorizationResultBasedKnexEntityLoader';
import {
FieldEqualityCondition,
QuerySelectionModifiers,
QuerySelectionModifiersWithOrderByFragment,
QuerySelectionModifiersWithOrderByRaw,
} from './BasePostgresEntityDatabaseAdapter';
AuthorizationResultBasedKnexEntityLoader,
EntityLoaderQuerySelectionModifiers,
EntityLoaderQuerySelectionModifiersWithOrderByFragment,
EntityLoaderQuerySelectionModifiersWithOrderByRaw,
} from './AuthorizationResultBasedKnexEntityLoader';
import { FieldEqualityCondition } from './BasePostgresEntityDatabaseAdapter';
import { BaseSQLQueryBuilder } from './BaseSQLQueryBuilder';
import { SQLFragment } from './SQLOperator';

Expand Down Expand Up @@ -52,8 +52,11 @@ export class EnforcingKnexEntityLoader<
*/
async loadFirstByFieldEqualityConjunctionAsync<N extends keyof Pick<TFields, TSelectedFields>>(
fieldEqualityOperands: FieldEqualityCondition<TFields, N>[],
querySelectionModifiers: Omit<QuerySelectionModifiers<TFields>, 'limit'> &
Required<Pick<QuerySelectionModifiers<TFields>, 'orderBy'>>,
querySelectionModifiers: Omit<
EntityLoaderQuerySelectionModifiers<TFields, TSelectedFields>,
'limit'
> &
Required<Pick<EntityLoaderQuerySelectionModifiers<TFields, TSelectedFields>, 'orderBy'>>,
): Promise<TEntity | null> {
const entityResult = await this.knexEntityLoader.loadFirstByFieldEqualityConjunctionAsync(
fieldEqualityOperands,
Expand All @@ -74,7 +77,7 @@ export class EnforcingKnexEntityLoader<
*/
async loadManyByFieldEqualityConjunctionAsync<N extends keyof Pick<TFields, TSelectedFields>>(
fieldEqualityOperands: FieldEqualityCondition<TFields, N>[],
querySelectionModifiers: QuerySelectionModifiers<TFields> = {},
querySelectionModifiers: EntityLoaderQuerySelectionModifiers<TFields, TSelectedFields> = {},
): Promise<readonly TEntity[]> {
const entityResults = await this.knexEntityLoader.loadManyByFieldEqualityConjunctionAsync(
fieldEqualityOperands,
Expand Down Expand Up @@ -116,7 +119,10 @@ export class EnforcingKnexEntityLoader<
async loadManyByRawWhereClauseAsync(
rawWhereClause: string,
bindings: any[] | object,
querySelectionModifiers: QuerySelectionModifiersWithOrderByRaw<TFields> = {},
querySelectionModifiers: EntityLoaderQuerySelectionModifiersWithOrderByRaw<
TFields,
TSelectedFields
> = {},
): Promise<readonly TEntity[]> {
const entityResults = await this.knexEntityLoader.loadManyByRawWhereClauseAsync(
rawWhereClause,
Expand Down Expand Up @@ -147,7 +153,10 @@ export class EnforcingKnexEntityLoader<
*/
loadManyBySQL(
fragment: SQLFragment,
modifiers: QuerySelectionModifiersWithOrderByFragment<TFields> = {},
modifiers: EntityLoaderQuerySelectionModifiersWithOrderByFragment<
TFields,
TSelectedFields
> = {},
): EnforcingSQLQueryBuilder<
TFields,
TIDField,
Expand Down Expand Up @@ -177,7 +186,7 @@ export class EnforcingSQLQueryBuilder<
TSelectedFields
>,
TSelectedFields extends keyof TFields,
> extends BaseSQLQueryBuilder<TFields, TEntity> {
> extends BaseSQLQueryBuilder<TFields, TSelectedFields, TEntity> {
constructor(
private readonly knexEntityLoader: AuthorizationResultBasedKnexEntityLoader<
TFields,
Expand All @@ -188,7 +197,7 @@ export class EnforcingSQLQueryBuilder<
TSelectedFields
>,
sqlFragment: SQLFragment,
modifiers: QuerySelectionModifiersWithOrderByFragment<TFields>,
modifiers: EntityLoaderQuerySelectionModifiersWithOrderByFragment<TFields, TSelectedFields>,
) {
super(sqlFragment, modifiers);
}
Expand Down
Loading