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
2 changes: 1 addition & 1 deletion packages/language/src/validators/datamodel-validator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ export default class DataModelValidator implements AstValidator<DataModel> {
if (field.type.array && !isDataModel(field.type.reference?.ref)) {
const provider = this.getDataSourceProvider(AstUtils.getContainerOfType(field, isModel)!);
if (provider === 'sqlite') {
accept('error', `Array type is not supported for "${provider}" provider.`, { node: field.type });
accept('error', `List type is not supported for "${provider}" provider.`, { node: field.type });
}
}

Expand Down
7 changes: 6 additions & 1 deletion packages/runtime/src/client/client-impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -556,7 +556,12 @@ function createModelCrudHandler<Schema extends SchemaDef, Model extends GetModel
},

groupBy: (args: unknown) => {
return createPromise('groupBy', args, new GroupByOperationHandler<Schema>(client, model, inputValidator));
return createPromise(
'groupBy',
args,
new GroupByOperationHandler<Schema>(client, model, inputValidator),
true,
);
},
} as ModelOperations<Schema, Model>;
}
11 changes: 11 additions & 0 deletions packages/runtime/src/client/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,14 @@ export const TRANSACTION_UNSUPPORTED_METHODS = ['$transaction', '$disconnect', '
* Prefix for JSON field used to store joined delegate rows.
*/
export const DELEGATE_JOINED_FIELD_PREFIX = '$delegate$';

/**
* Logical combinators used in filters.
*/
export const LOGICAL_COMBINATORS = ['AND', 'OR', 'NOT'] as const;

/**
* Aggregation operators.
*/
export const AGGREGATE_OPERATORS = ['_count', '_sum', '_avg', '_min', '_max'] as const;
export type AGGREGATE_OPERATORS = (typeof AGGREGATE_OPERATORS)[number];
9 changes: 5 additions & 4 deletions packages/runtime/src/client/contract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -752,17 +752,18 @@ export type ModelOperations<Schema extends SchemaDef, Model extends GetModels<Sc
* _count: true
* }); // result: `Array<{ country: string, city: string, _count: number }>`
*
* // group by with sorting, the `orderBy` fields must be in the `by` list
* // group by with sorting, the `orderBy` fields must be either an aggregation
* // or a field used in the `by` list
* await db.profile.groupBy({
* by: 'country',
* orderBy: { country: 'desc' }
* });
*
* // group by with having (post-aggregation filter), the `having` fields must
* // be in the `by` list
* // group by with having (post-aggregation filter), the fields used in `having` must
* // be either an aggregation, or a field used in the `by` list
* await db.profile.groupBy({
* by: 'country',
* having: { country: 'US' }
* having: { country: 'US', age: { _avg: { gte: 18 } } }
* });
*/
groupBy<T extends GroupByArgs<Schema, Model>>(
Expand Down
145 changes: 104 additions & 41 deletions packages/runtime/src/client/crud-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,7 @@ export type WhereInput<
Schema extends SchemaDef,
Model extends GetModels<Schema>,
ScalarOnly extends boolean = false,
WithAggregations extends boolean = false,
> = {
[Key in GetModelFields<Schema, Model> as ScalarOnly extends true
? Key extends RelationFields<Schema, Model>
Expand All @@ -223,7 +224,12 @@ export type WhereInput<
: FieldIsArray<Schema, Model, Key> extends true
? ArrayFilter<GetModelFieldType<Schema, Model, Key>>
: // primitive
PrimitiveFilter<Schema, GetModelFieldType<Schema, Model, Key>, ModelFieldIsOptional<Schema, Model, Key>>;
PrimitiveFilter<
Schema,
GetModelFieldType<Schema, Model, Key>,
ModelFieldIsOptional<Schema, Model, Key>,
WithAggregations
>;
} & {
$expr?: (eb: ExpressionBuilder<ToKyselySchema<Schema>, Model>) => OperandExpression<SqlBool>;
} & {
Expand All @@ -249,38 +255,56 @@ type ArrayFilter<T extends string> = {
isEmpty?: boolean;
};

type PrimitiveFilter<Schema extends SchemaDef, T extends string, Nullable extends boolean> = T extends 'String'
? StringFilter<Schema, Nullable>
type PrimitiveFilter<
Schema extends SchemaDef,
T extends string,
Nullable extends boolean,
WithAggregations extends boolean,
> = T extends 'String'
? StringFilter<Schema, Nullable, WithAggregations>
: T extends 'Int' | 'Float' | 'Decimal' | 'BigInt'
? NumberFilter<Schema, T, Nullable>
? NumberFilter<Schema, T, Nullable, WithAggregations>
: T extends 'Boolean'
? BooleanFilter<Nullable>
? BooleanFilter<Schema, Nullable, WithAggregations>
: T extends 'DateTime'
? DateTimeFilter<Schema, Nullable>
? DateTimeFilter<Schema, Nullable, WithAggregations>
: T extends 'Bytes'
? BytesFilter<Nullable>
? BytesFilter<Schema, Nullable, WithAggregations>
: T extends 'Json'
? 'Not implemented yet' // TODO: Json filter
: never;

type CommonPrimitiveFilter<Schema extends SchemaDef, DataType, T extends BuiltinType, Nullable extends boolean> = {
type CommonPrimitiveFilter<
Schema extends SchemaDef,
DataType,
T extends BuiltinType,
Nullable extends boolean,
WithAggregations extends boolean,
> = {
equals?: NullableIf<DataType, Nullable>;
in?: DataType[];
notIn?: DataType[];
lt?: DataType;
lte?: DataType;
gt?: DataType;
gte?: DataType;
not?: PrimitiveFilter<Schema, T, Nullable>;
not?: PrimitiveFilter<Schema, T, Nullable, WithAggregations>;
};

export type StringFilter<Schema extends SchemaDef, Nullable extends boolean> =
export type StringFilter<Schema extends SchemaDef, Nullable extends boolean, WithAggregations extends boolean> =
| NullableIf<string, Nullable>
| (CommonPrimitiveFilter<Schema, string, 'String', Nullable> & {
| (CommonPrimitiveFilter<Schema, string, 'String', Nullable, WithAggregations> & {
contains?: string;
startsWith?: string;
endsWith?: string;
} & (ProviderSupportsCaseSensitivity<Schema> extends true
} & (WithAggregations extends true
? {
_count?: NumberFilter<Schema, 'Int', false, false>;
_min?: StringFilter<Schema, false, false>;
_max?: StringFilter<Schema, false, false>;
}
: {}) &
(ProviderSupportsCaseSensitivity<Schema> extends true
? {
mode?: 'default' | 'insensitive';
}
Expand All @@ -290,27 +314,58 @@ export type NumberFilter<
Schema extends SchemaDef,
T extends 'Int' | 'Float' | 'Decimal' | 'BigInt',
Nullable extends boolean,
> = NullableIf<number | bigint, Nullable> | CommonPrimitiveFilter<Schema, number, T, Nullable>;
WithAggregations extends boolean,
> =
| NullableIf<number | bigint, Nullable>
| (CommonPrimitiveFilter<Schema, number, T, Nullable, WithAggregations> &
(WithAggregations extends true
? {
_count?: NumberFilter<Schema, 'Int', false, false>;
_avg?: NumberFilter<Schema, T, false, false>;
_sum?: NumberFilter<Schema, T, false, false>;
_min?: NumberFilter<Schema, T, false, false>;
_max?: NumberFilter<Schema, T, false, false>;
}
: {}));

export type DateTimeFilter<Schema extends SchemaDef, Nullable extends boolean> =
export type DateTimeFilter<Schema extends SchemaDef, Nullable extends boolean, WithAggregations extends boolean> =
| NullableIf<Date | string, Nullable>
| CommonPrimitiveFilter<Schema, Date | string, 'DateTime', Nullable>;
| (CommonPrimitiveFilter<Schema, Date | string, 'DateTime', Nullable, WithAggregations> &
(WithAggregations extends true
? {
_count?: NumberFilter<Schema, 'Int', false, false>;
_min?: DateTimeFilter<Schema, false, false>;
_max?: DateTimeFilter<Schema, false, false>;
}
: {}));

export type BytesFilter<Nullable extends boolean> =
export type BytesFilter<Schema extends SchemaDef, Nullable extends boolean, WithAggregations extends boolean> =
| NullableIf<Uint8Array | Buffer, Nullable>
| {
| ({
equals?: NullableIf<Uint8Array, Nullable>;
in?: Uint8Array[];
notIn?: Uint8Array[];
not?: BytesFilter<Nullable>;
};
not?: BytesFilter<Schema, Nullable, WithAggregations>;
} & (WithAggregations extends true
? {
_count?: NumberFilter<Schema, 'Int', false, false>;
_min?: BytesFilter<Schema, false, false>;
_max?: BytesFilter<Schema, false, false>;
}
: {}));

export type BooleanFilter<Nullable extends boolean> =
export type BooleanFilter<Schema extends SchemaDef, Nullable extends boolean, WithAggregations extends boolean> =
| NullableIf<boolean, Nullable>
| {
| ({
equals?: NullableIf<boolean, Nullable>;
not?: BooleanFilter<Nullable>;
};
not?: BooleanFilter<Schema, Nullable, WithAggregations>;
} & (WithAggregations extends true
? {
_count?: NumberFilter<Schema, 'Int', false, false>;
_min?: BooleanFilter<Schema, false, false>;
_max?: BooleanFilter<Schema, false, false>;
}
: {}));

export type SortOrder = 'asc' | 'desc';
export type NullsOrder = 'first' | 'last';
Expand Down Expand Up @@ -340,14 +395,15 @@ export type OrderBy<
: {}) &
(WithAggregation extends true
? {
_count?: OrderBy<Schema, Model, WithRelation, false>;
_count?: OrderBy<Schema, Model, false, false>;
_min?: MinMaxInput<Schema, Model, SortOrder>;
_max?: MinMaxInput<Schema, Model, SortOrder>;
} & (NumericFields<Schema, Model> extends never
? {}
: {
_avg?: SumAvgInput<Schema, Model>;
_sum?: SumAvgInput<Schema, Model>;
_min?: MinMaxInput<Schema, Model>;
_max?: MinMaxInput<Schema, Model>;
// aggregations specific to numeric fields
_avg?: SumAvgInput<Schema, Model, SortOrder>;
_sum?: SumAvgInput<Schema, Model, SortOrder>;
})
: {});

Expand Down Expand Up @@ -931,13 +987,13 @@ export type AggregateArgs<Schema extends SchemaDef, Model extends GetModels<Sche
orderBy?: OrArray<OrderBy<Schema, Model, true, false>>;
} & {
_count?: true | CountAggregateInput<Schema, Model>;
_min?: MinMaxInput<Schema, Model, true>;
_max?: MinMaxInput<Schema, Model, true>;
} & (NumericFields<Schema, Model> extends never
? {}
: {
_avg?: SumAvgInput<Schema, Model>;
_sum?: SumAvgInput<Schema, Model>;
_min?: MinMaxInput<Schema, Model>;
_max?: MinMaxInput<Schema, Model>;
_avg?: SumAvgInput<Schema, Model, true>;
_sum?: SumAvgInput<Schema, Model, true>;
});

type NumericFields<Schema extends SchemaDef, Model extends GetModels<Schema>> = keyof {
Expand All @@ -952,16 +1008,16 @@ type NumericFields<Schema extends SchemaDef, Model extends GetModels<Schema>> =
: never]: GetModelField<Schema, Model, Key>;
};

type SumAvgInput<Schema extends SchemaDef, Model extends GetModels<Schema>> = {
[Key in NumericFields<Schema, Model>]?: true;
type SumAvgInput<Schema extends SchemaDef, Model extends GetModels<Schema>, ValueType> = {
[Key in NumericFields<Schema, Model>]?: ValueType;
};

type MinMaxInput<Schema extends SchemaDef, Model extends GetModels<Schema>> = {
type MinMaxInput<Schema extends SchemaDef, Model extends GetModels<Schema>, ValueType> = {
[Key in GetModelFields<Schema, Model> as FieldIsArray<Schema, Model, Key> extends true
? never
: FieldIsRelation<Schema, Model, Key> extends true
? never
: Key]?: true;
: Key]?: ValueType;
};

export type AggregateResult<
Expand Down Expand Up @@ -1006,21 +1062,28 @@ type AggCommonOutput<Input> = Input extends true

// #region GroupBy

type GroupByHaving<Schema extends SchemaDef, Model extends GetModels<Schema>> = Omit<
WhereInput<Schema, Model, true, true>,
'$expr'
>;

export type GroupByArgs<Schema extends SchemaDef, Model extends GetModels<Schema>> = {
where?: WhereInput<Schema, Model>;
orderBy?: OrArray<OrderBy<Schema, Model, false, true>>;
by: NonRelationFields<Schema, Model> | NonEmptyArray<NonRelationFields<Schema, Model>>;
having?: WhereInput<Schema, Model, true>;
having?: GroupByHaving<Schema, Model>;
take?: number;
skip?: number;
// aggregations
_count?: true | CountAggregateInput<Schema, Model>;
_min?: MinMaxInput<Schema, Model, true>;
_max?: MinMaxInput<Schema, Model, true>;
} & (NumericFields<Schema, Model> extends never
? {}
: {
_avg?: SumAvgInput<Schema, Model>;
_sum?: SumAvgInput<Schema, Model>;
_min?: MinMaxInput<Schema, Model>;
_max?: MinMaxInput<Schema, Model>;
// aggregations specific to numeric fields
_avg?: SumAvgInput<Schema, Model, true>;
_sum?: SumAvgInput<Schema, Model, true>;
});

export type GroupByResult<
Expand Down
Loading