如何使用 TypeORM 在 NestJS 中实现分页

How to implement pagination in NestJS with TypeORM

有什么方法可以通过单个查询获取总计数和记录,而不是 运行 将其作为 2 个单独的查询?

如果不可能,有没有办法在两个查询中重用 where 条件?

async findAll(query): Promise<Paginate> {
  const take = query.take || 10
  const skip = query.skip || 0
  const keyword = query.keyword || ''

  const builder = this.userRepository.createQueryBuilder("user")
  const total = await builder.where("user.name like :name", { name: '%' + keyword + '%' }).getCount()
  const data = await builder.where("user.name like :name", { name: '%' + keyword + '%' }).orderBy('name', 'DESC').skip(skip).take(take).getMany();

  return {
    data: data,
    count: total
  }
}

{
  count: 10,
  data: [
    {
      id: 1,
      name: 'David'
    },
    {
      id: 2,
      name: 'Alex'
    }]
}

你可以在这个 project 中找到一些很好的例子。简而言之 typeorm 有一个非常好的方法特定于这个用例 findAndCount.

async findAll(query): Promise<Paginate> {
    const take = query.take || 10
    const skip = query.skip || 0
    const keyword = query.keyword || ''

    const [result, total] = await this.userRepository.findAndCount(
        {
            where: { name: Like('%' + keyword + '%') }, order: { name: "DESC" },
            take: take,
            skip: skip
        }
    );

    return {
        data: result,
        count: total
    }
}

存储库 API 您可以找到 here. More documentation about Repository class can be found here

如果您需要对许多记录进行分页,即多次迭代,(可能在迁移或批量更新期间)。

async getPaginatedResults(query: any, transactionManager?: EntityManager): Promise<any> {

}

总结...

此中间件会检查您是否在 URL 中有 take 和 skip 参数,如果有,它会从字符串转换为数字,如果您不使用默认值。 10 表示接受,0 表示跳过。

take是每页的结果数,skip,应该从哪里开始读取记录。

有了这个,我设置为 GET 方法拦截 "product / paged" 路由。

有了这个,我可以在控制器中检索这些值并传递给 TypeORM 或 SQL 查询。

@Injectable()
export class PagerMiddleware implements NestMiddleware {
  use(req: any, res: any, next: () => void) {
    req.query.take = +req.query.take || 10;
    req.query.skip = +req.query.skip || 0;
    next();
  }
}

并在模块中申请。

export class AdminFeatureApi implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    consumer.apply(PagerMiddleware)
    .forRoutes({ path: 'product/paged', method: RequestMethod.GET })
  }
}

控制器

@Controller('product')
export class TrainingDomainController {
  constructor(private service: YourService) {}

  @Get('paged')
  get(@Query() { take, skip }) {
    return this.service.findAll(take, skip);
  }
}

和服务

@Injectable()
export class YourService {
  constructor(
    @InjectRepository(YourEntity)
    private readonly repo: MongoRepository<YourEntity>
  ) {}

  async findAll(take: number = 10, skip: number = 0) {
    const [data, total] = await this.repo.findAndCount({ take, skip });
    return { data, total };
  }
}

好吗?

你也可以看看这个 NestJS 和 TypeORM 包:

https://github.com/nestjsx/nestjs-typeorm-paginate

我更喜欢使用页面而不是直接跳过

  • 端点示例:/users?page=4&take=3

    async findAll(query): Promise<Paginate> {
        const take = query.take || 10
        const page=query.page || 1;
        const skip= (page-1) * take ;
        const keyword = query.keyword || ''
    
        const [result, total] = await this.userRepository.findAndCount(
            {
                where: { name: Like('%' + keyword + '%') }, order: { name: "DESC" },
                take: take,
                skip: skip
            }
        );
    
        return {
            data: result,
            count: total
        }
    }
    

    2/。更好的方法(处理响应):

     async findAll(query): Promise<Paginate> {
         const take= query.take || 10
         const page=query.page || 1;
         const skip= (page-1) * take ;
         const keyword = query.keyword || ''
    
         const data = await this.userRepository.findAndCount(
             {
                 where: { name: Like('%' + keyword + '%') }, order: { name: "DESC" },
                 take: take,
                 skip: skip
             }
         );
         return paginateResponse(data ,page,take)
    
     }
    
    export function paginateResponse(data,page,limit) {
      const [result, total]=data;
      const lastPage=Math.ceil(total/limit);
      const nextPage=page+1 >lastPage ? null :page+1;
      const prevPage=page-1 < 1 ? null :page-1;
      return {
        statusCode: 'success',
        data: [...result],
        count: total,
        currentPage: page,
        nextPage: nextPage,
        prevPage: prevPage,
        lastPage: lastPage,
      }
    }