perf: optimize listWithTeamHandler query (#22069)

* perf: optimize listWithTeamHandler query

* chore: update query

* chore: add accepted true

* Update packages/trpc/server/routers/viewer/eventTypes/listWithTeam.handler.ts

* test: add integration test for the sql query

---------

Co-authored-by: Alex van Andel <me@alexvanandel.com>
This commit is contained in:
Udit Takkar
2025-06-30 18:35:49 +00:00
committed by GitHub
co-authored by Alex van Andel
parent ae383164bc
commit 55cc1aea59
2 changed files with 173 additions and 28 deletions
@@ -0,0 +1,148 @@
import type { User, Team, EventType } from "@prisma/client";
import { describe, it, expect, afterAll, beforeAll } from "vitest";
import prisma from "@calcom/prisma";
import { MembershipRole } from "@calcom/prisma/enums";
import { listWithTeamHandler } from "./listWithTeam.handler";
let user1: User;
let user2: User;
let team1: Team;
let team2: Team;
let eventType1: EventType;
let eventType2: EventType;
let eventType3: EventType;
let eventType4: EventType;
describe("listWithTeamHandler", () => {
beforeAll(async () => {
// Create users, teams and event types
user1 = await prisma.user.create({
data: {
username: "testuser-lwt-1",
email: "testuser-lwt-1@example.com",
name: "Test User 1",
},
});
user2 = await prisma.user.create({
data: {
username: "testuser-lwt-2",
email: "testuser-lwt-2@example.com",
name: "Test User 2",
},
});
team1 = await prisma.team.create({
data: {
name: "Team 1 lwt",
slug: "team-1-lwt",
members: {
create: {
userId: user1.id,
role: MembershipRole.MEMBER,
accepted: true,
},
},
},
});
team2 = await prisma.team.create({
data: {
name: "Team 2 lwt",
slug: "team-2-lwt",
},
});
eventType1 = await prisma.eventType.create({
data: {
title: "User 1 Event",
slug: "user1-event-lwt",
length: 30,
userId: user1.id,
},
});
eventType2 = await prisma.eventType.create({
data: {
title: "Team 1 Event",
slug: "team1-event-lwt",
length: 30,
teamId: team1.id,
userId: user1.id,
},
});
eventType3 = await prisma.eventType.create({
data: {
title: "User 2 Event",
slug: "user2-event-lwt",
length: 30,
userId: user2.id,
},
});
eventType4 = await prisma.eventType.create({
data: {
title: "Team 2 Event",
slug: "team2-event-lwt",
length: 30,
teamId: team2.id,
userId: user2.id,
},
});
});
afterAll(async () => {
await prisma.eventType.deleteMany({
where: {
id: {
in: [eventType1.id, eventType2.id, eventType3.id, eventType4.id],
},
},
});
await prisma.team.deleteMany({
where: {
id: {
in: [team1.id, team2.id],
},
},
});
await prisma.user.deleteMany({
where: {
id: {
in: [user1.id, user2.id],
},
},
});
});
it("should return user's own event types and event types of teams they are a member of", async () => {
const result = await listWithTeamHandler({
ctx: {
// we only need the id from the user object
user: { id: user1.id } as any,
},
});
expect(result).toHaveLength(2);
const resultIds = result.map((e) => e.id);
expect(resultIds).toContain(eventType1.id);
expect(resultIds).toContain(eventType2.id);
const eventType1Result = result.find((e) => e.id === eventType1.id);
expect(eventType1Result).toBeDefined();
expect(eventType1Result?.title).toBe(eventType1.title);
expect(eventType1Result?.slug).toBe(eventType1.slug);
expect(eventType1Result?.team).toBeNull();
const eventType2Result = result.find((e) => e.id === eventType2.id);
expect(eventType2Result).toBeDefined();
expect(eventType2Result?.title).toBe(eventType2.title);
expect(eventType2Result?.slug).toBe(eventType2.slug);
expect(eventType2Result?.team).toBeDefined();
expect(eventType2Result?.team?.id).toBe(team1.id);
expect(eventType2Result?.team?.name).toBe(team1.name);
});
});
@@ -1,4 +1,6 @@
import { prisma } from "@calcom/prisma";
import { Prisma } from "@prisma/client";
import db from "@calcom/prisma";
import type { TrpcSessionUser } from "../../../types";
@@ -9,31 +11,26 @@ type ListWithTeamOptions = {
};
export const listWithTeamHandler = async ({ ctx }: ListWithTeamOptions) => {
return await prisma.eventType.findMany({
where: {
OR: [
{ userId: ctx.user.id },
{
team: {
members: {
some: {
userId: ctx.user.id,
},
},
},
},
],
},
select: {
id: true,
team: {
select: {
id: true,
name: true,
},
},
title: true,
slug: true,
},
});
const userId = ctx.user.id;
const query = Prisma.sql`SELECT "public"."EventType"."id", "public"."EventType"."teamId", "public"."EventType"."title", "public"."EventType"."slug", "j1"."name" as "teamName"
FROM "public"."EventType"
LEFT JOIN "public"."Team" AS "j1" ON ("j1"."id") = ("public"."EventType"."teamId")
WHERE "public"."EventType"."userId" = ${userId}
UNION
SELECT "public"."EventType"."id", "public"."EventType"."teamId", "public"."EventType"."title", "public"."EventType"."slug", "j1"."name" as "teamName"
FROM "public"."EventType"
INNER JOIN "public"."Team" AS "j1" ON ("j1"."id") = ("public"."EventType"."teamId")
INNER JOIN "public"."Membership" AS "t2" ON "t2"."teamId" = "j1"."id"
WHERE "t2"."userId" = ${userId} AND "t2"."accepted" = true`;
const result = await db.$queryRaw<
{ id: number; teamId: number | null; title: string; slug: string; teamName: string | null }[]
>(query);
return result.map((row) => ({
id: row.id,
team: row.teamId ? { id: row.teamId, name: row.teamName || "" } : null,
title: row.title,
slug: row.slug,
}));
};