/// <summary> /// Applies the pagination algorithm to the provided data. /// </summary> /// <param name="query">The query builder.</param> /// <param name="arguments">The paging arguments.</param> /// <param name="totalCount">Specify the total amount of elements</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns></returns> public async ValueTask <CollectionSegment <TEntity> > ApplyPaginationAsync( TQuery query, OffsetPagingArguments arguments, int?totalCount, CancellationToken cancellationToken) { Func <CancellationToken, ValueTask <int> > getTotalCount = totalCount is null ? async ct => await CountAsync(query, ct) : _ => new ValueTask <int>(totalCount.Value); TQuery sliced = query; if (arguments.Skip is { } skip) { sliced = ApplySkip(sliced, skip); } if (arguments.Take is { } take) { sliced = ApplyTake(sliced, take + 1); } IReadOnlyList <TEntity> items = await ExecuteAsync(sliced, cancellationToken).ConfigureAwait(false); bool hasNextPage = items.Count == arguments.Take + 1; bool hasPreviousPage = (arguments.Skip ?? 0) > 0; CollectionSegmentInfo pageInfo = new(hasNextPage, hasPreviousPage); items = new SkipLastCollection <TEntity>(items, skipLast: hasNextPage); return(new CollectionSegment <TEntity>( items, pageInfo, getTotalCount)); }
/// <summary> /// Applies the pagination algorithm to the provided query. /// </summary> /// <param name="query">The query builder.</param> /// <param name="arguments">The paging arguments.</param> /// <param name="totalCount">Specify the total amount of elements</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns> /// Returns the connection. /// </returns> public async ValueTask <Connection <TEntity> > ApplyPaginationAsync( TQuery query, CursorPagingArguments arguments, int?totalCount, CancellationToken cancellationToken) { if (query is null) { throw new ArgumentNullException(nameof(query)); } var maxElementCount = int.MaxValue; Func <CancellationToken, ValueTask <int> > executeCount = totalCount is null ? ct => CountAsync(query, ct) : _ => new ValueTask <int>(totalCount.Value); // We only need the maximal element count if no `before` counter is set and no `first` // argument is provided. if (arguments.Before is null && arguments.First is null) { var count = await executeCount(cancellationToken); maxElementCount = count; // in case we already know the total count, we override the countAsync parameter // so that we do not have to fetch the count twice executeCount = _ => new ValueTask <int>(count); } CursorPagingRange range = SliceRange(arguments, maxElementCount); var skip = range.Start; var take = range.Count(); // we fetch one element more than we requested if (take != maxElementCount) { take++; } TQuery slicedSource = query; if (skip != 0) { slicedSource = ApplySkip(query, skip); } if (take != maxElementCount) { slicedSource = ApplyTake(slicedSource, take); } IReadOnlyList <Edge <TEntity> > selectedEdges = await ExecuteAsync(slicedSource, skip, cancellationToken); var moreItemsReturnedThanRequested = selectedEdges.Count > range.Count(); var isSequenceFromStart = range.Start == 0; selectedEdges = new SkipLastCollection <Edge <TEntity> >( selectedEdges, moreItemsReturnedThanRequested); ConnectionPageInfo pageInfo = CreatePageInfo(isSequenceFromStart, moreItemsReturnedThanRequested, selectedEdges); return(new Connection <TEntity>(selectedEdges, pageInfo, executeCount)); }