typeorm-cursor-pagination icon indicating copy to clipboard operation
typeorm-cursor-pagination copied to clipboard

How about supporting Raw result and join query?

Open yearsalary opened this issue 2 years ago • 8 comments

example:

const queryBuilder = this.dataSource .createQueryBuilder(Notification, 'notification') .select('notification.id', 'id') .addSelect('message.title', 'title') .leftJoin('notification.receiver', 'user') .leftJoin('notification.message', 'message') .where('user.did =:did', { did: userDID }); const paginator = buildPaginator({ entity: Notification, paginationKeys: ['id'], query: pagingQuery, }); const { data, cursor } = await paginator.paginateRaw(queryBuilder); return { data, cursor };

yearsalary avatar Oct 31 '22 11:10 yearsalary

and,,, typeorm querybuilder.take method is not working if with join query https://github.com/typeorm/typeorm/issues/4742

yearsalary avatar Oct 31 '22 11:10 yearsalary

Sorry, I don’t get it, could you please explain it more?

benjamin658 avatar Nov 04 '22 06:11 benjamin658

Sorry, I don’t get it, could you please explain it more?

ok, In typeorm, to getting raw result use getRawMany method. (https://typeorm.io/select-query-builder#getting-raw-results) In this package, Paginater.paginate method call getMany in typeorm to find entities. To not only support entities but also raw result, add Paginater.paginateRaw and call getRawMany.

yearsalary avatar Nov 07 '22 11:11 yearsalary

But if it returns raw results, then the library cannot generate cursors.

benjamin658 avatar Nov 09 '22 07:11 benjamin658

yes, so how about add paginateRaw that return generate cursor like paginate in your package like this.

public async paginateRaw(builder: SelectQueryBuilder<Entity>): Promise<PagingRawResult> {
        const raws = await this.appendPagingQuery(builder).getRawMany();
        const hasMore = raws.length > this.limit;

        if (hasMore) {
            raws.splice(raws.length - 1, 1);
        }

        if (raws.length === 0) {
            return this.toPagingResult(raws);
        }

        if (!this.hasAfterCursor() && this.hasBeforeCursor()) {
            raws.reverse();
        }

        if (this.hasBeforeCursor() || hasMore) {
            this.nextAfterCursor = this.encode(raws[raws.length - 1]);
        }

        if (this.hasAfterCursor() || (hasMore && this.hasBeforeCursor())) {
            this.nextBeforeCursor = this.encode(raws[0]);
        }

        return this.toPagingRawResult(raws);
    } 
   
private toPagingRawResult(raws: any[]): PagingRawResult {
        return {
            data: raws,
            cursor: this.getCursor(),
        };
    }    

this paginateRaw method logic equal paginate in your package except below code.

        const raws = await this.appendPagingQuery(builder).getRawMany();

yearsalary avatar Nov 15 '22 04:11 yearsalary

Would you mind sending a PR for this feature with proper test cases?

benjamin658 avatar Nov 16 '22 07:11 benjamin658

I've had this need as well so I had to implement myself, and I can tell it's not easy to do with typeorm, the mapping from raw results to entity results for cursor creation is tedious. About JOIN queries, that just requires adding code to handle these cases as the number of results in page will be inconsistent when used with limit and offset.

amiryadid-fb avatar Dec 06 '22 08:12 amiryadid-fb

For solving the join query, I notice Typeorm provides this Joining and mapping functionality. So in your case where you want to load the message: .leftJoin('notification.message', 'message') can be written to .leftJoinAndMapOne('notification.message', 'notification.message', 'message')

In this case the Message entity will be loaded in the PagingResulst

NotificationEntity {
  id: '123',
  ...
  message: MessageEntity {
    id: '123'
    ...
  }
}

artekr avatar Oct 06 '23 16:10 artekr