Skip to content

Commit

Permalink
Merge pull request #6 from koyopro/feature/association_condition
Browse files Browse the repository at this point in the history
Make it possible to pass associations to the 'where' condition.
  • Loading branch information
koyopro authored May 26, 2024
2 parents 8652908 + fab1990 commit 70714a7
Show file tree
Hide file tree
Showing 4 changed files with 97 additions and 17 deletions.
57 changes: 53 additions & 4 deletions packages/accel-record-core/src/relation/where.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,16 @@ export class Where {
const newOptions = JSON.parse(JSON.stringify(this.options));
for (const key in input) {
const column = this.model.attributeToColumn(key);
if (!column) throw new Error(`Attribute not found: ${key}`);
if (Array.isArray(input[key])) {
if (!column) {
const associationWheres = this.getAssocationWhere(key, input[key]);
if (associationWheres) {
for (const where of associationWheres) {
newOptions["wheres"].push(where);
}
} else {
throw new Error(`Attribute not found: ${key}`);
}
} else if (Array.isArray(input[key])) {
newOptions["wheres"].push([column, "in", input[key]]);
} else if (input[key] != null && typeof input[key] === "object") {
for (const operator in input[key]) {
Expand All @@ -59,6 +67,35 @@ export class Where {
return new Relation(this.model, newOptions);
}

private getAssocationWhere<T, M extends ModelMeta>(
this: Relation<T, M>,
key: Extract<keyof M["WhereInput"], string>,
value: any,
op: string = "in"
) {
const field = this.model.findField(key);
if (field?.relationName == undefined) return;

const where: any = {};

const records = [value].flat();
for (let i = 0; i < field.foreignKeys.length; i++) {
const column = this.model.attributeToColumn(field.foreignKeys[i]);
if (!column) return;
where[column] ||= [];

for (const record of records) {
where[column].push(record[field.primaryKeys[i]]);
}
}

return Object.entries(where).map(([column, values]) => [
column,
op,
values,
]);
}

/**
* Adds a "where not" condition to the current relation.
*
Expand All @@ -72,8 +109,20 @@ export class Where {
const newOptions = JSON.parse(JSON.stringify(this.options));
for (const key in input) {
const column = this.model.attributeToColumn(key);
if (!column) throw new Error(`Attribute not found: ${key}`);
if (input[key] != null && typeof input[key] === "object") {
if (!column) {
const associationWheres = this.getAssocationWhere(
key,
input[key],
"not in"
);
if (associationWheres) {
for (const where of associationWheres) {
newOptions["wheres"].push(where);
}
} else {
throw new Error(`Attribute not found: ${key}`);
}
} else if (input[key] != null && typeof input[key] === "object") {
for (const operator in input[key]) {
if (operator === "in") {
newOptions["wheres"].push([column, "not in", input[key][operator]]);
Expand Down
33 changes: 20 additions & 13 deletions packages/prisma-generator-accel-record/src/generators/type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -161,18 +161,6 @@ declare module "accel-record" {
return ` & ({ ${f.name}: ${f.type} } | { ${foreignKeys} })`;
})
.join("");
const whereInputs =
model.fields
.filter(reject)
.filter(
(field) => field.relationName == undefined && field.type != "Json"
)
.map((field) => {
const type = field.typeName;
const filter = getFilterType(type);
return `\n ${field.name}?: ${type} | ${type}[] | ${filter} | null;`;
})
.join("") + "\n ";
const orderInputs =
model.fields
.filter(reject)
Expand All @@ -197,7 +185,7 @@ type ${model.meta} = {
CreateInput: {
${columns}
}${associationColumns};
WhereInput: {${whereInputs}};
WhereInput: {${whereInputs(model)}};
OrderInput: {${orderInputs}};
};
registerModel(${model.persistedModel});
Expand Down Expand Up @@ -297,3 +285,22 @@ const columnDefines = (model: ModelWrapper) =>
};`;
})
.join("\n");

const whereInputs = (model: ModelWrapper) =>
model.fields
.filter(
(field) =>
field.relationName == undefined ||
(field.relationFromFields?.length ?? 0) > 0
)
.filter((field) => field.type != "Json")
.map((field) => {
const type = field.typeName;
const filter = getFilterType(type);
if (field.relationName) {
if (field.name == "posts") console.log(field);
return `\n ${field.name}?: ${field.type} | ${field.type}[];`;
}
return `\n ${field.name}?: ${type} | ${type}[] | ${filter} | null;`;
})
.join("") + "\n ";
6 changes: 6 additions & 0 deletions tests/models/_types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,9 @@ type UserTeamMeta = {
assignedBy: string;
} & ({ user: User } | { userId: number }) & ({ team: Team } | { teamId: number });
WhereInput: {
user?: User | User[];
userId?: number | number[] | Filter<number> | null;
team?: Team | Team[];
teamId?: number | number[] | Filter<number> | null;
assignedAt?: Date | Date[] | Filter<Date> | null;
assignedBy?: string | string[] | StringFilter | null;
Expand Down Expand Up @@ -239,6 +241,7 @@ type PostMeta = {
title?: string | string[] | StringFilter | null;
content?: string | string[] | StringFilter | null;
published?: boolean | boolean[] | undefined | null;
author?: User | User[];
authorId?: number | number[] | Filter<number> | null;
};
OrderInput: {
Expand Down Expand Up @@ -321,6 +324,7 @@ type SettingMeta = {
} & ({ user: User } | { userId: number });
WhereInput: {
settingId?: number | number[] | Filter<number> | null;
user?: User | User[];
userId?: number | number[] | Filter<number> | null;
threshold?: number | number[] | Filter<number> | null;
createdAt?: Date | Date[] | Filter<Date> | null;
Expand Down Expand Up @@ -372,6 +376,7 @@ type ProfileMeta = {
} & ({ user: User } | { userId: number });
WhereInput: {
id?: number | number[] | Filter<number> | null;
user?: User | User[];
userId?: number | number[] | Filter<number> | null;
bio?: string | string[] | StringFilter | null;
point?: number | number[] | Filter<number> | null;
Expand Down Expand Up @@ -461,6 +466,7 @@ type EmployeeMeta = {
id?: number | number[] | Filter<number> | null;
name?: string | string[] | StringFilter | null;
companyId?: number | number[] | Filter<number> | null;
company?: Company | Company[];
};
OrderInput: {
id?: SortOrder;
Expand Down
18 changes: 18 additions & 0 deletions tests/models/relation.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,15 @@ describe("Relation", () => {
).toHaveLength(2);
});

test("#where() with association", () => {
const users = $user.createList(2);
$post.createList(2, { author: users[0] });
$post.createList(1, { author: users[1] });
expect(Post.all().where({ author: users[0] }).count()).toBe(2);
expect(Post.all().where({ author: users }).count()).toBe(3);
expect(Post.all().where({ author: [] }).count()).toBe(0);
});

test("#whereNot()", () => {
$user.create({ name: "hoge", age: 20 });
$user.create({ name: "fuga", age: 30 });
Expand All @@ -152,6 +161,15 @@ describe("Relation", () => {
expect(User.all().whereNot({ age: null }).first()?.name).toBe("hoge");
});

test("#whereNot() with association", () => {
const users = $user.createList(2);
$post.createList(2, { author: users[0] });
$post.createList(1, { author: users[1] });
expect(Post.all().whereNot({ author: users[0] }).count()).toBe(1);
expect(Post.all().whereNot({ author: users }).count()).toBe(0);
expect(Post.all().whereNot({ author: [] }).count()).toBe(3);
});

test("#whereRaw()", () => {
$user.create({ name: "hoge", age: 20 });
$user.create({ name: "fuga", age: 30 });
Expand Down

0 comments on commit 70714a7

Please sign in to comment.