【问题标题】: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,
        };
    }

【问题讨论】:

    标签: postgresql graphql typeorm apollo-server typegraphql


    【解决方案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,
              };
          }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2016-03-21
        • 2016-06-13
        • 1970-01-01
        • 1970-01-01
        • 2015-04-05
        • 2013-05-04
        • 2012-12-01
        相关资源
        最近更新 更多