/// <summary> /// Applies the offset pagination algorithm to the <paramref name="query"/>. /// </summary> /// <param name="query"> /// The query on which the the offset pagination algorithm shall be applied to. /// </param> /// <param name="context"> /// The field resolver context. /// </param> /// <param name="defaultPageSize"> /// The default page size if no boundaries are set. /// </param> /// <param name="totalCount"> /// The total count if already known. /// </param> /// <param name="cancellationToken"> /// The cancellation token. /// </param> /// <typeparam name="TEntity"> /// The entity type. /// </typeparam> /// <returns> /// Returns a collection segment instance that represents the result of applying the /// offset paging algorithm to the provided <paramref name="query"/>. /// </returns> public static ValueTask <CollectionSegment <TEntity> > ApplyOffsetPaginationAsync <TEntity>( this IQueryable <TEntity> query, IResolverContext context, int?defaultPageSize = null, int?totalCount = null, CancellationToken cancellationToken = default) { if (query is null) { throw new ArgumentNullException(nameof(query)); } if (context is null) { throw new ArgumentNullException(nameof(context)); } var skip = context.ArgumentValue <int?>(OffsetPagingArgumentNames.Skip); var take = context.ArgumentValue <int?>(OffsetPagingArgumentNames.Take) ?? defaultPageSize; var arguments = new OffsetPagingArguments(skip, take); if (totalCount is null && context.IsTotalCountSelected()) { totalCount = query.Count(); } return(QueryableOffsetPagination <TEntity> .Instance.ApplyPaginationAsync( query, arguments, totalCount, cancellationToken)); }
/// <summary> /// Applies the offset pagination algorithm to the <paramref name="query"/>. /// </summary> /// <param name="query"> /// The query on which the the offset pagination algorithm shall be applied to. /// </param> /// <param name="arguments"> /// The offset paging arguments. /// </param> /// <param name="cancellationToken"> /// The cancellation token. /// </param> /// <typeparam name="TEntity"> /// The entity type. /// </typeparam> /// <returns> /// Returns a collection segment instance that represents the result of applying the /// offset paging algorithm to the provided <paramref name="query"/>. /// </returns> public static ValueTask <CollectionSegment <TEntity> > ApplyOffsetPaginationAsync <TEntity>( this IQueryable <TEntity> query, OffsetPagingArguments arguments, CancellationToken cancellationToken = default) => QueryableOffsetPagination <TEntity> .Instance.ApplyPaginationAsync( query, arguments, cancellationToken);
protected override ValueTask <CollectionSegment> SliceAsync( IResolverContext context, object source, OffsetPagingArguments arguments) { IMongoPagingContainer <TEntity> f = CreatePagingContainer(source); return(ResolveAsync(context, f, arguments)); }
async ValueTask <IPage> IPagingHandler.SliceAsync( IResolverContext context, object source) { var skip = context.ArgumentValue <int?>(OffsetPagingArgumentNames.Skip); var take = context.ArgumentValue <int?>(OffsetPagingArgumentNames.Take); var arguments = new OffsetPagingArguments(skip, take ?? DefaultPageSize); return(await SliceAsync(context, source, arguments).ConfigureAwait(false)); }
public static OffsetPagingArguments GetOffsetPagingArgsSafely(this IResolverContext context, int?defaultSkip = null, int?defaultTake = null) { int?skip = context?.ArgumentValueSafely <int?>(OffsetPagingArgNames.Skip); int?take = context?.ArgumentValueSafely <int?>(OffsetPagingArgNames.Take); //Initialize with default values that enable default behaviour to retrieve all results anytime // the values are not specified; consistent with Cursor based paging where all params are optional. var pagingArgs = new OffsetPagingArguments(skip ?? defaultSkip, take ?? defaultTake); return(pagingArgs); }
/// <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)); }
protected override ValueTask <CollectionSegment> SliceAsync(IResolverContext context, object source, OffsetPagingArguments arguments) { //If Appropriate we handle the values here to ensure that no post-processing is done other than // correctly mapping the results into a GraphQL Collection Segment with appropriate Paging Details... if (source is IPreProcessedOffsetPageResults <TEntity> pagedResults) { bool includeTotalCountEnabled = this.PagingOptions.IncludeTotalCount ?? PagingDefaults.IncludeTotalCount; var graphQLParamsContext = new GraphQLParamsContext(context); //Optimized to only require TotalCount value if the query actually requested it! if (includeTotalCountEnabled && graphQLParamsContext.IsTotalCountRequested && pagedResults.TotalCount == null) { throw new InvalidOperationException($"Total Count is requested in the query, but was not provided with the results [{this.GetType().GetTypeName()}] from the resolvers pre-processing logic; TotalCount is null."); } int?totalCount = pagedResults.TotalCount; //Ensure we are null safe and return a valid empty list by default. var segmentResults = pagedResults?.ToList() ?? new List <TEntity>(); var collectionSegmentInfo = new CollectionSegmentInfo( hasNextPage: pagedResults?.HasNextPage ?? false, hasPreviousPage: pagedResults?.HasPreviousPage ?? false ); var graphQLConnection = new CollectionSegment( (IReadOnlyCollection <object>)segmentResults, collectionSegmentInfo, ct => new ValueTask <int>(totalCount ?? throw new InvalidOperationException()) ); return(new ValueTask <CollectionSegment>(graphQLConnection)); } throw new GraphQLException($"[{nameof(PreProcessedOffsetPagingHandler<TEntity>)}] cannot handle the specified data source of type [{source.GetType().Name}]."); }
/// <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="cancellationToken">The cancellation token.</param> /// <returns></returns> public ValueTask <CollectionSegment <TEntity> > ApplyPaginationAsync( TQuery query, OffsetPagingArguments arguments, CancellationToken cancellationToken) => ApplyPaginationAsync(query, arguments, null, cancellationToken);
/// <summary> /// The algorithm defining how to slice data of the specified <paramref name="source"/>. /// </summary> /// <param name="context"> /// The resolver context of the execution field. /// </param> /// <param name="source"> /// The object representing the data source, collection, or query builder. /// </param> /// <param name="arguments"> /// The paging arguments provided by the user. /// </param> /// <returns> /// The <see cref="CollectionSegment"/> representing /// the slice of items belonging to the requested page. /// </returns> protected abstract ValueTask <CollectionSegment> SliceAsync( IResolverContext context, object source, OffsetPagingArguments arguments);
public static IOffsetPageResults <T> SliceAsOffsetPage <T>(this IEnumerable <T> items, OffsetPagingArguments graphQLPagingArgs, bool includeTotalCount = true) where T : class { return(items.SliceAsOffsetPage( skip: graphQLPagingArgs.Skip, take: graphQLPagingArgs.Take, includeTotalCount: includeTotalCount )); }