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:
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user