refactor: Simplify query with conditional joins chain

- Use single query builder with conditional joins after .from()
- Cleaner code: no duplicated logic between expand/no-expand cases
- Still only 1 query when not expanding, joins only when needed
- Remove unused imports (and, inArray)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
John Yeo
2025-10-14 10:46:47 +01:00
parent d0ba936a92
commit d344d55a33

View File

@@ -7,7 +7,7 @@ import {
organizations,
user as userTable,
} from "@autumn/shared";
import { and, eq, inArray } from "drizzle-orm";
import { eq } from "drizzle-orm";
import { createRoute } from "@/honoMiddlewares/routeHandler.js";
/**
@@ -23,23 +23,46 @@ export const listPlatformUsers = createRoute({
const shouldExpandOrgs = query.expand?.includes("organizations");
// Build query with conditional joins
let baseQuery = db
.select({
userId: userTable.id,
userName: userTable.name,
userEmail: userTable.email,
userCreatedAt: userTable.createdAt,
...(shouldExpandOrgs && {
orgSlug: organizations.slug,
orgName: organizations.name,
orgCreatedAt: organizations.createdAt,
}),
})
.from(userTable);
// Conditionally add joins only when expanding orgs
if (shouldExpandOrgs) {
baseQuery = baseQuery
.innerJoin(member, eq(member.userId, userTable.id))
.innerJoin(
organizations,
eq(organizations.id, member.organizationId),
) as typeof baseQuery;
}
// Apply filters and pagination
const results = await baseQuery
.where(
shouldExpandOrgs
? eq(organizations.created_by, org.id)
: eq(userTable.createdBy, org.id),
)
.limit(query.limit)
.offset(query.offset);
logger.info(`Found ${results.length} platform users`);
if (!shouldExpandOrgs) {
// Simple case: just get users without organizations (no join needed)
const usersData = await db
.select({
userId: userTable.id,
userName: userTable.name,
userEmail: userTable.email,
userCreatedAt: userTable.createdAt,
})
.from(userTable)
.where(eq(userTable.createdBy, org.id))
.limit(query.limit)
.offset(query.offset);
logger.info(`Found ${usersData.length} platform users`);
const users: ApiPlatformUser[] = usersData.map((userData) => ({
// Simple case: just map users directly
const users: ApiPlatformUser[] = results.map((userData) => ({
name: userData.userName,
email: userData.userEmail,
created_at: userData.userCreatedAt.getTime(),
@@ -53,52 +76,7 @@ export const listPlatformUsers = createRoute({
});
}
// Complex case: get users WITH their organizations
// First, get the paginated user IDs (no join needed - users have created_by)
const paginatedUsers = await db
.select({
userId: userTable.id,
})
.from(userTable)
.where(eq(userTable.createdBy, org.id))
.limit(query.limit)
.offset(query.offset);
if (paginatedUsers.length === 0) {
return c.json({
list: [],
total: 0,
limit: query.limit,
offset: query.offset,
});
}
const userIds = paginatedUsers.map((u) => u.userId);
// Now fetch all user and org data in one query
const allData = await db
.select({
userId: userTable.id,
userName: userTable.name,
userEmail: userTable.email,
userCreatedAt: userTable.createdAt,
orgSlug: organizations.slug,
orgName: organizations.name,
orgCreatedAt: organizations.createdAt,
})
.from(userTable)
.innerJoin(member, eq(member.userId, userTable.id))
.innerJoin(organizations, eq(organizations.id, member.organizationId))
.where(
and(
eq(organizations.created_by, org.id),
inArray(userTable.id, userIds),
),
);
logger.info(`Found ${allData.length} user-org relationships`);
// Group data by user
// Complex case: group organizations by user
const usersMap = new Map<
string,
{
@@ -109,7 +87,7 @@ export const listPlatformUsers = createRoute({
}
>();
for (const row of allData) {
for (const row of results) {
if (!usersMap.has(row.userId)) {
usersMap.set(row.userId, {
name: row.userName,
@@ -122,7 +100,7 @@ export const listPlatformUsers = createRoute({
const userData = usersMap.get(row.userId)!;
// Limit to 100 organizations per user
if (userData.organizations.length < 100) {
if (userData.organizations.length < 100 && row.orgSlug) {
// Remove the master org slug prefix from the organization slug
let cleanedSlug = row.orgSlug;
const prefix = `${org.id}_`;
@@ -137,16 +115,14 @@ export const listPlatformUsers = createRoute({
userData.organizations.push({
slug: cleanedSlug,
name: row.orgName,
created_at: row.orgCreatedAt.getTime(),
name: row.orgName!,
created_at: row.orgCreatedAt!.getTime(),
});
}
}
// Convert map to array, preserving pagination order
const users: ApiPlatformUser[] = userIds
.map((userId) => usersMap.get(userId))
.filter((user): user is NonNullable<typeof user> => user !== undefined);
// Convert map to array
const users: ApiPlatformUser[] = Array.from(usersMap.values());
return c.json({
list: users,