You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将Drizzle Query Builder结果映射为嵌套对象?

从NestJS-TypeORM迁移至Drizzle ORM:Query Builder结果转嵌套格式的最佳实践

我正从NestJS-TypeORM迁移至Drizzle ORM,遇到以下差异:

  • TypeORM的仓库与查询构建器返回结构相似的结果,便于处理关联数据。
  • Drizzle中,db.query()返回规整的嵌套数据(比如用户及其关联帖子的分组结构),但Query Builder(db.select())返回原始扁平数据,需要额外处理才能实现类似嵌套结构。

示例对比

db.query()返回结果

[
    {
        "id": 10,
        "name": "Dan",
        "posts": [
            {
                "id": 1,
                "content": "SQL is awesome",
                "authorId": 10
            },
            {
                "id": 2,
                "content": "But check relational queries",
                "authorId": 10
            }
        ]
    }
]

Query Builder返回结果

const result: {
    user: { id: number; name: string };
    pets: { id: number; name: string; ownerId: number } | null;
}[] = await db
    .select()
    .from(users)
    .leftJoin(pets, eq(users.id, pets.ownerId));

我希望找到一种机制或最佳实践,将Query Builder的结果映射为嵌套格式(类似db.query()的输出),保证API响应一致,无需客户端适配不同格式。请问有哪些建议、模式或库推荐?在Drizzle项目中是如何处理此问题的?


解决方案建议

1. 自定义分组映射函数

这是最直接的方式,手动遍历扁平结果,按关联主键分组构建嵌套结构,适合简单关联场景,无需额外依赖。

示例代码(用户-宠物关联):

interface FlatUserPet {
  user: { id: number; name: string };
  pets: { id: number; name: string; ownerId: number } | null;
}

interface NestedUser {
  id: number;
  name: string;
  pets: { id: number; name: string; ownerId: number }[];
}

function mapToNestedUsers(flatResults: FlatUserPet[]): NestedUser[] {
  const userMap = new Map<number, NestedUser>();

  for (const item of flatResults) {
    const { user, pets } = item;
    // 初始化用户条目
    if (!userMap.has(user.id)) {
      userMap.set(user.id, {
        ...user,
        pets: []
      });
    }
    // 添加关联宠物(排除null情况)
    if (pets) {
      userMap.get(user.id)!.pets.push(pets);
    }
  }

  return Array.from(userMap.values());
}

// 使用方式
const flatResult = await db.select().from(users).leftJoin(pets, eq(users.id, pets.ownerId));
const nestedResult = mapToNestedUsers(flatResult);

2. 复用Drizzle的关联定义逻辑

如果必须使用Query Builder(比如需要复杂自定义SQL),可以结合Drizzle的relations实体关系定义,参考db.query的内部分组逻辑,封装通用映射工具。db.query本质是执行SQL后自动完成分组嵌套,你可以提取这部分逻辑复用,避免重复造轮子。

3. 第三方数据转换库简化复杂场景

对于多层级关联(如用户-帖子-评论),手动写映射函数会繁琐,可借助专门的转换库:

  • Zod:兼顾数据校验与格式转换,通过transform方法实现扁平到嵌套的转换
    示例:
    import { z } from 'zod';
    
    const flatUserPetSchema = z.object({
      user: z.object({ id: z.number(), name: z.string() }),
      pets: z.object({ id: z.number(), name: z.string(), ownerId: z.number() }).nullable()
    });
    
    const nestedUserSchema = z.object({
      id: z.number(),
      name: z.string(),
      pets: z.array(z.object({ id: z.number(), name: z.string(), ownerId: z.number() }))
    });
    
    function mapToNestedUsersWithZod(flatResults: z.infer<typeof flatUserPetSchema>[]) {
      const userMap = new Map<number, z.infer<typeof nestedUserSchema>>();
      for (const item of flatResults) {
        const user = item.user;
        if (!userMap.has(user.id)) {
          userMap.set(user.id, { ...user, pets: [] });
        }
        if (item.pets) {
          userMap.get(user.id)!.pets.push(item.pets);
        }
      }
      return nestedUserSchema.array().parse(Array.from(userMap.values()));
    }
    
  • Class-transformer:结合NestJS生态,通过DTO类装饰器完成转换
    示例:
    import { Transform, Type, plainToInstance } from 'class-transformer';
    
    class PetDto {
      id: number;
      name: string;
      ownerId: number;
    }
    
    class UserDto {
      id: number;
      name: string;
    
      @Transform(({ obj }) => obj.pets ? [obj.pets] : [], { toClassOnly: true })
      @Type(() => PetDto)
      pets: PetDto[];
    }
    
    // 使用方式
    const flatResult = await db.select().from(users).leftJoin(pets, eq(users.id, pets.ownerId));
    const grouped = mapToNestedUsers(flatResult);
    const nestedResult = plainToInstance(UserDto, grouped);
    

4. 封装NestJS查询复用层

在NestJS项目中,可封装DrizzleQueryService自定义服务,提供selectWithNested方法,内部执行Query Builder查询后自动调用映射函数,统一处理转换逻辑,避免重复代码。

项目实际处理方式

在我维护的Drizzle项目中:

  • 简单关联场景用自定义映射函数,轻便灵活;
  • 复杂关联场景结合Zod做校验+转换,保证数据正确性;
  • 性能敏感场景直接用db.query,复用其优化后的自动分组逻辑,减少额外处理成本。

内容的提问来源于stack exchange,提问作者BereketeAb B. Kebebew

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 00:41:07