Skip to content

Commit

Permalink
feat: add orderByRaw to loadManyByRawWhereClauseAsync (#185)
Browse files Browse the repository at this point in the history
* feat: add orderByRaw to querySelectionModifiers

* implement PR feedback

* fix tsc

* restrict orderByRaw to loadManyByRawWhereClauseAsync

* create new type for orderbyraw param
  • Loading branch information
FiberJW authored Aug 9, 2022
1 parent af81981 commit 2817d78
Show file tree
Hide file tree
Showing 6 changed files with 63 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
TableQuerySelectionModifiers,
TableFieldSingleValueEqualityCondition,
TableFieldMultiValueEqualityCondition,
TableQuerySelectionModifiersWithOrderByRaw,
} from '@expo/entity';
import { Knex } from 'knex';

Expand Down Expand Up @@ -53,6 +54,20 @@ export default class PostgresEntityDatabaseAdapter<TFields> extends EntityDataba
);
}

private applyQueryModifiersToQueryOrderByRaw(
query: Knex.QueryBuilder,
querySelectionModifiers: TableQuerySelectionModifiersWithOrderByRaw
): Knex.QueryBuilder {
let ret = this.applyQueryModifiersToQuery(query, querySelectionModifiers);

const { orderByRaw } = querySelectionModifiers;
if (orderByRaw !== undefined) {
ret = ret.orderByRaw(orderByRaw);
}

return ret;
}

private applyQueryModifiersToQuery(
query: Knex.QueryBuilder,
querySelectionModifiers: TableQuerySelectionModifiers
Expand Down Expand Up @@ -129,13 +144,13 @@ export default class PostgresEntityDatabaseAdapter<TFields> extends EntityDataba
tableName: string,
rawWhereClause: string,
bindings: object | any[],
querySelectionModifiers: TableQuerySelectionModifiers
querySelectionModifiers: TableQuerySelectionModifiersWithOrderByRaw
): Promise<object[]> {
let query = queryInterface
.select()
.from(tableName)
.whereRaw(rawWhereClause, bindings as any);
query = this.applyQueryModifiersToQuery(query, querySelectionModifiers);
query = this.applyQueryModifiersToQueryOrderByRaw(query, querySelectionModifiers);
return await wrapNativePostgresCallAsync(() => query);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -379,6 +379,15 @@ describe('postgres entity integration', () => {

expect(resultsMultipleOrderBy).toHaveLength(3);
expect(resultsMultipleOrderBy.map((e) => e.getField('name'))).toEqual(['c', 'b', 'a']);

const resultsOrderByRaw = await PostgresTestEntity.loader(vc1)
.enforcing()
.loadManyByRawWhereClauseAsync('has_a_dog = ?', [true], {
orderByRaw: 'has_a_dog ASC, name DESC',
});

expect(resultsOrderByRaw).toHaveLength(3);
expect(resultsOrderByRaw.map((e) => e.getField('name'))).toEqual(['c', 'b', 'a']);
});
});

Expand Down
8 changes: 6 additions & 2 deletions packages/entity/src/EnforcingEntityLoader.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
import { FieldEqualityCondition, QuerySelectionModifiers } from './EntityDatabaseAdapter';
import {
FieldEqualityCondition,
QuerySelectionModifiers,
QuerySelectionModifiersWithOrderByRaw,
} from './EntityDatabaseAdapter';
import EntityLoader from './EntityLoader';
import EntityPrivacyPolicy from './EntityPrivacyPolicy';
import ReadonlyEntity from './ReadonlyEntity';
Expand Down Expand Up @@ -132,7 +136,7 @@ export default class EnforcingEntityLoader<
async loadManyByRawWhereClauseAsync(
rawWhereClause: string,
bindings: any[] | object,
querySelectionModifiers: QuerySelectionModifiers<TFields> = {}
querySelectionModifiers: QuerySelectionModifiersWithOrderByRaw<TFields> = {}
): Promise<readonly TEntity[]> {
const entityResults = await this.entityLoader.loadManyByRawWhereClauseAsync(
rawWhereClause,
Expand Down
29 changes: 25 additions & 4 deletions packages/entity/src/EntityDatabaseAdapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,14 @@ export interface QuerySelectionModifiers<TFields> {
limit?: number;
}

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

export interface TableQuerySelectionModifiers {
orderBy:
| {
Expand All @@ -79,6 +87,10 @@ export interface TableQuerySelectionModifiers {
limit: number | undefined;
}

export interface TableQuerySelectionModifiersWithOrderByRaw extends TableQuerySelectionModifiers {
orderByRaw: string | undefined;
}

/**
* A database adapter is an interface by which entity objects can be
* fetched, inserted, updated, and deleted from a database. This base class
Expand Down Expand Up @@ -149,7 +161,7 @@ export default abstract class EntityDatabaseAdapter<TFields> {
*
* @param queryContext - query context with which to perform the fetch
* @param fieldEqualityOperands - list of field equality where clause operand specifications
* @param querySelectionModifiers - limit, offset, and orderBy for the query
* @param querySelectionModifiers - limit, offset, orderBy, and orderByRaw for the query
* @returns array of objects matching the query
*/
async fetchManyByFieldEqualityConjunctionAsync<N extends keyof TFields>(
Expand Down Expand Up @@ -207,14 +219,14 @@ export default abstract class EntityDatabaseAdapter<TFields> {
queryContext: EntityQueryContext,
rawWhereClause: string,
bindings: any[] | object,
querySelectionModifiers: QuerySelectionModifiers<TFields>
querySelectionModifiers: QuerySelectionModifiersWithOrderByRaw<TFields>
): Promise<readonly Readonly<TFields>[]> {
const results = await this.fetchManyByRawWhereClauseInternalAsync(
queryContext.getQueryInterface(),
this.entityConfiguration.tableName,
rawWhereClause,
bindings,
this.convertToTableQueryModifiers(querySelectionModifiers)
this.convertToTableQueryModifiersWithOrderByRaw(querySelectionModifiers)
);

return results.map((result) =>
Expand All @@ -227,7 +239,7 @@ export default abstract class EntityDatabaseAdapter<TFields> {
tableName: string,
rawWhereClause: string,
bindings: any[] | object,
querySelectionModifiers: TableQuerySelectionModifiers
querySelectionModifiers: TableQuerySelectionModifiersWithOrderByRaw
): Promise<object[]>;

/**
Expand Down Expand Up @@ -363,6 +375,15 @@ export default abstract class EntityDatabaseAdapter<TFields> {
id: any
): Promise<number>;

private convertToTableQueryModifiersWithOrderByRaw(
querySelectionModifiers: QuerySelectionModifiersWithOrderByRaw<TFields>
): TableQuerySelectionModifiersWithOrderByRaw {
return {
...this.convertToTableQueryModifiers(querySelectionModifiers),
orderByRaw: querySelectionModifiers.orderByRaw,
};
}

private convertToTableQueryModifiers(
querySelectionModifiers: QuerySelectionModifiers<TFields>
): TableQuerySelectionModifiers {
Expand Down
5 changes: 3 additions & 2 deletions packages/entity/src/EntityLoader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
FieldEqualityCondition,
QuerySelectionModifiers,
isSingleValueFieldEqualityCondition,
QuerySelectionModifiersWithOrderByRaw,
} from './EntityDatabaseAdapter';
import EntityPrivacyPolicy, { EntityPrivacyPolicyEvaluationContext } from './EntityPrivacyPolicy';
import { EntityQueryContext } from './EntityQueryContext';
Expand Down Expand Up @@ -249,7 +250,7 @@ export default class EntityLoader<
*
* @param rawWhereClause - parameterized SQL WHERE clause with positional binding placeholders or named binding placeholders
* @param bindings - array of positional bindings or object of named bindings
* @param querySelectionModifiers - limit, offset, and orderBy for the query
* @param querySelectionModifiers - limit, offset, orderBy, and orderByRaw for the query
* @returns array of entity results that match the query, where result error can be UnauthorizedError
* @throws Error when rawWhereClause or bindings are invalid
*
Expand All @@ -258,7 +259,7 @@ export default class EntityLoader<
async loadManyByRawWhereClauseAsync(
rawWhereClause: string,
bindings: any[] | object,
querySelectionModifiers: QuerySelectionModifiers<TFields> = {}
querySelectionModifiers: QuerySelectionModifiersWithOrderByRaw<TFields> = {}
): Promise<readonly Result<TEntity>[]> {
const fieldObjects = await this.dataManager.loadManyByRawWhereClauseAsync(
this.queryContext,
Expand Down
5 changes: 3 additions & 2 deletions packages/entity/src/internal/EntityDataManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import DataLoader from 'dataloader';
import EntityDatabaseAdapter, {
FieldEqualityCondition,
QuerySelectionModifiers,
QuerySelectionModifiersWithOrderByRaw,
} from '../EntityDatabaseAdapter';
import { EntityQueryContext } from '../EntityQueryContext';
import EntityQueryContextProvider from '../EntityQueryContextProvider';
Expand Down Expand Up @@ -173,14 +174,14 @@ export default class EntityDataManager<TFields> {
* @param queryContext - query context in which to perform the load
* @param rawWhereClause - parameterized SQL WHERE clause with positional binding placeholders or named binding placeholders
* @param bindings - array of positional bindings or object of named bindings
* @param querySelectionModifiers - limit, offset, and orderBy for the query
* @param querySelectionModifiers - limit, offset, orderBy, and orderByRaw for the query
* @returns array of objects matching the query
*/
async loadManyByRawWhereClauseAsync(
queryContext: EntityQueryContext,
rawWhereClause: string,
bindings: any[] | object,
querySelectionModifiers: QuerySelectionModifiers<TFields>
querySelectionModifiers: QuerySelectionModifiersWithOrderByRaw<TFields>
): Promise<readonly Readonly<TFields>[]> {
return await timeAndLogLoadEventAsync(
this.metricsAdapter,
Expand Down

0 comments on commit 2817d78

Please sign in to comment.