如何对搜索结果进行分页?

Posted

技术标签:

【中文标题】如何对搜索结果进行分页?【英文标题】:How to paginate searched results? 【发布时间】:2021-05-22 08:21:42 【问题描述】:

如何使用光标对搜索结果进行分页?

这是我的数据查询功能,有问题,因为当我加载更多数据时,有些项目与搜索的查询无关?

    @UseMiddleware(isAuthenticated)
    @Query(() => PaginatedQuizzes)
    async quizzes(
        @Arg('limit', () => Int) limit: number,
        @Arg('cursor', () => String,  nullable: true ) cursor: string | null,
        @Arg('query', () => String,  nullable: true ) query: string | null
    ): Promise<PaginatedQuizzes> 
        const realLimit = Math.min(50, limit);
        const realLimitPlusOne = realLimit + 1;

        const qs = await getConnection()
            .getRepository(Quiz)
            .createQueryBuilder('q')
            .leftJoinAndSelect('q.author', 'author')
            .leftJoinAndSelect('q.questions', 'questions')

        if (query) 
            const formattedQuery = query.trim().replace(/ /g, ' <-> ');

            qs.where(
                `to_tsvector('simple',q.title) @@ to_tsquery('simple', :query)`,
                
                    query: `$formattedQuery:*`,
                
            ).orWhere(
                `to_tsvector('simple',q.description) @@ to_tsquery('simple', :query)`,
                
                    query: `$formattedQuery:*`,
                
            );
        

        if (cursor) 
            qs.where('q."created_at" < :cursor', 
                cursor: new Date(parseInt(cursor)),
            );
        

        const quizzes = await qs
            .orderBy('q.created_at', 'DESC')
            .take(realLimitPlusOne)
            .getMany();

        return 
            quizzes: (quizzes as [Quiz]).slice(0, realLimit),
            hasMore: (quizzes as [Quiz]).length === realLimitPlusOne,
        ;
    

【问题讨论】:

【参考方案1】:

您可以使用自定义系统,而不是安装可能存在漏洞的繁重软件包。

这是一个示例controller,带有limitpage 参数,用于在x 页面上获取n 元素

@Get('elements/:limit/:page')
  getElementsWithPagination(
    @Param('limit') limit: number,
    @Param('page') page: number,
  ): Promise<ElementsEntity[]> 
    return this.elementsService.getElementsWithPagination(limit, page);
  

这是访问您的存储库的service

async getElementsWithPagination(
    limit: number,
    page: number,
  ): Promise<ElementsEntity[]> 
    return this.elementsRepository.findElementsWithPagination(limit, page);
  

最后是带有查询参数的repository,从最新到最旧排序

async findElementsWithPagination(limit, page): Promise<ElementsEntity[]> 
    return this.find(
      take: limit,
      skip: limit * (page - 1),
      order:  createdAt: 'DESC' ,
    );
  

借助这个系统,您可以逐页查询n元素。

【讨论】:

大型结果集怎么样?在丢弃skip指定的行数之前,不采取并跳过将整个结果集加载到内存中吗?如果您有多个 10k 行的结果集怎么办?【参考方案2】:

嘿,我一直在使用这个很酷的包对表格进行分页检查here

import  buildPaginator  from 'typeorm-cursor-pagination';

const paginator = buildPaginator(
  entity: User,
  paginationKeys: ['id'],
  query: 
    limit: 10,
    order: 'ASC',
  ,
);
const  data, cursor  = await paginator.paginate(queryBuilder);

【讨论】:

【参考方案3】:

非常感谢您的回复,但我可以使用 find 选项自行解决,

    @UseMiddleware(isAuthenticated)
    @Query(() => PaginatedQuizzes)
    async quizzes(
        @Arg('limit', () => Int) limit: number,
        @Arg('cursor', () => String,  nullable: true ) cursor: string | null,
        @Arg('query', () => String,  nullable: true ) query: string | null
    ): Promise<PaginatedQuizzes> 
        const realLimit = Math.min(20, limit);
        const realLimitPlusOne = realLimit + 1;

        let findOptionInitial: FindManyOptions = 
            relations: [
                'author',
                'questions',
            ],
            order: 
                created_at: 'DESC',
            ,
            take: realLimitPlusOne,
        ;

        let findOption: FindManyOptions;

        if (cursor && query) 
            const formattedQuery = query.trim().replace(/ /g, ' <-> ');

            findOption = 
                ...findOptionInitial,
                where: [
                    
                        description: Raw(
                            (description) =>
                                `to_tsvector('simple', $description) @@ to_tsquery('simple', :query)`,
                            
                                query: formattedQuery,
                            
                        ),
                        created_at: LessThan(new Date(parseInt(cursor))),
                    ,
                    
                        title: Raw(
                            (title) =>
                                `to_tsvector('simple', $title) @@ to_tsquery('simple', :query)`,
                            
                                query: formattedQuery,
                            
                        ),
                        created_at: LessThan(new Date(parseInt(cursor))),
                    ,
                ],
            ;
         else if (cursor) 
            findOption = 
                ...findOptionInitial,
                where: 
                    created_at: LessThan(new Date(parseInt(cursor))),
                ,
            ;
         else if (query) 
            const formattedQuery = query.trim().replace(/ /g, ' <-> ');

            findOption = 
                ...findOptionInitial,
                where: [
                    
                        description: Raw(
                            (description) =>
                                `to_tsvector('simple', $description) @@ to_tsquery('simple', :query)`,
                            
                                query: formattedQuery,
                            
                        ),
                    ,
                    
                        title: Raw(
                            (title) =>
                                `to_tsvector('simple', $title) @@ to_tsquery('simple', :query)`,
                            
                                query: formattedQuery,
                            
                        ),
                    ,
                ],
            ;
         else 
            findOption = findOptionInitial;
        

        const quizzes = await Quiz.find(findOption as FindManyOptions);

        return 
            quizzes: (quizzes as [Quiz]).slice(0, realLimit),
            hasMore: (quizzes as [Quiz]).length === realLimitPlusOne,
        ;
    

【讨论】:

以上是关于如何对搜索结果进行分页?的主要内容,如果未能解决你的问题,请参考以下文章

我们如何使用 Flask 对 SQL Server 搜索结果进行分页

如何使用 Laravel 和 jQuery 对查询结果进行分页

如何在 Reactive Extensions 中组合两个 observables 以对结果进行分页?

如何在 MongoDB 中对聚合查询结果进行分页并获得总文档数(Node.js + Mongoose)?

如何对 Stack Exchange Data Explorer (SEDE) 结果进行分页?

如何对 Infinite Scroll 的查询结果进行分页?