Skip to content

Commit

Permalink
Do not inline total count by default into paging queries. (#7942)
Browse files Browse the repository at this point in the history
  • Loading branch information
michaelstaib committed Jan 22, 2025
1 parent 4eb11c3 commit 2d9d70e
Show file tree
Hide file tree
Showing 11 changed files with 162 additions and 68 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ public interface IQueryableExecutable<T> : IExecutable<T>
/// <returns>The new instance of an enumerable executable</returns>
IQueryableExecutable<T> WithSource(IQueryable<T> source);


/// <summary>
/// Returns a new executable with the provided source
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@ public static class CursorPagingRequestExecutorBuilderExtension
/// <param name="defaultProvider">
/// Defines if the registered provider shall be registered as the default provider.
/// </param>
/// <param name="inlineTotalCount">
/// Specifies that the paging provider shall inline the total count query into the
/// sliced query in order to have a single database call.
/// Some database providers might not support this feature.
/// </param>
/// <returns>
/// The request executor builder.
/// </returns>
Expand All @@ -31,9 +36,11 @@ public static class CursorPagingRequestExecutorBuilderExtension
public static IRequestExecutorBuilder AddQueryableCursorPagingProvider(
this IRequestExecutorBuilder builder,
string? providerName = null,
bool defaultProvider = false)
bool defaultProvider = false,
bool? inlineTotalCount = null)
=> AddCursorPagingProvider<QueryableCursorPagingProvider>(
builder,
_ => new QueryableCursorPagingProvider(inlineTotalCount),
providerName,
defaultProvider);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,19 @@

namespace HotChocolate.Types.Pagination;

internal sealed class QueryableCursorPagingHandler<TEntity>(PagingOptions options)
: CursorPagingHandler<IQueryable<TEntity>, TEntity>(options)
internal sealed class QueryableCursorPagingHandler<TEntity> : CursorPagingHandler<IQueryable<TEntity>, TEntity>
{
private readonly bool? _inlineTotalCount;

public QueryableCursorPagingHandler(PagingOptions options) : this(options, false)
{
}

public QueryableCursorPagingHandler(PagingOptions options, bool? inlineTotalCount) : base(options)
{
_inlineTotalCount = inlineTotalCount;
}

private static readonly QueryableCursorPaginationAlgorithm<TEntity> _paginationAlgorithm =
QueryableCursorPaginationAlgorithm<TEntity>.Instance;

Expand All @@ -18,7 +28,7 @@ public ValueTask<Connection<TEntity>> SliceAsync(
source.Source,
arguments,
_paginationAlgorithm,
new QueryExecutor(source),
new QueryExecutor(source, _inlineTotalCount),
context.RequestAborted);

protected override ValueTask<Connection> SliceAsync(
Expand All @@ -42,11 +52,11 @@ private async ValueTask<Connection> SliceAsyncInternal(
source.Source,
arguments,
_paginationAlgorithm,
new QueryExecutor(source),
new QueryExecutor(source, _inlineTotalCount),
context.RequestAborted)
.ConfigureAwait(false);

private sealed class QueryExecutor(IQueryableExecutable<TEntity> executable)
private sealed class QueryExecutor(IQueryableExecutable<TEntity> executable, bool? allowInlining)
: ICursorPaginationQueryExecutor<IQueryable<TEntity>, TEntity>
{
public ValueTask<int> CountAsync(
Expand All @@ -66,17 +76,33 @@ public async ValueTask<CursorPaginationData<TEntity>> QueryAsync(

if (includeTotalCount)
{
var combinedQuery = slicedQuery.Select(t => new { TotalCount = originalQuery.Count(), Item = t });
totalCount = 0;
if (allowInlining ?? false)
{
var combinedQuery = slicedQuery.Select(t => new { TotalCount = originalQuery.Count(), Item = t });
totalCount = 0;

var index = offset;
await foreach (var item in executable
.WithSource(combinedQuery)
.ToAsyncEnumerable(cancellationToken)
.ConfigureAwait(false))
var index = offset;
await foreach (var item in executable
.WithSource(combinedQuery)
.ToAsyncEnumerable(cancellationToken)
.ConfigureAwait(false))
{
edges.Add(IndexEdge<TEntity>.Create(item.Item, index++));
totalCount = item.TotalCount;
}
}
else
{
edges.Add(IndexEdge<TEntity>.Create(item.Item, index++));
totalCount = item.TotalCount;
var index = offset;
await foreach (var item in executable
.WithSource(slicedQuery)
.ToAsyncEnumerable(cancellationToken)
.ConfigureAwait(false))
{
edges.Add(IndexEdge<TEntity>.Create(item, index++));
}

totalCount = await executable.CountAsync(cancellationToken).ConfigureAwait(false);
}
}
else
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System.Collections.Concurrent;
using System.Reflection;
using HotChocolate.Internal;

Expand All @@ -8,11 +9,21 @@ namespace HotChocolate.Types.Pagination;
/// </summary>
public class QueryableCursorPagingProvider : CursorPagingProvider
{
private static readonly ConcurrentDictionary<Type, MethodInfo> _factoryCache = new();
private readonly bool? _inlineTotalCount;

private static readonly MethodInfo _createHandler =
typeof(QueryableCursorPagingProvider).GetMethod(
nameof(CreateHandlerInternal),
BindingFlags.Static | BindingFlags.NonPublic)!;

public QueryableCursorPagingProvider() { }

public QueryableCursorPagingProvider(bool? inlineTotalCount)
{
_inlineTotalCount = inlineTotalCount;
}

/// <inheritdoc />
public override bool CanHandle(IExtendedType source)
{
Expand All @@ -34,12 +45,12 @@ protected override CursorPagingHandler CreateHandler(
throw new ArgumentNullException(nameof(source));
}

return (CursorPagingHandler)_createHandler
.MakeGenericMethod(source.ElementType?.Source ?? source.Source)
.Invoke(null, [options,])!;
var key = source.ElementType?.Source ?? source.Source;
var factory = _factoryCache.GetOrAdd(key, static type => _createHandler.MakeGenericMethod(type));
return (CursorPagingHandler)factory.Invoke(null, [options, _inlineTotalCount])!;
}

private static QueryableCursorPagingHandler<TEntity> CreateHandlerInternal<TEntity>(
PagingOptions options) =>
new(options);
PagingOptions options, bool? inlineTotalCount)
=> new(options, inlineTotalCount);
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,10 @@ private DatabaseContext<TResult> BuildContext<TResult>(
protected IRequestExecutor CreateSchema<TEntity>(TEntity[] entities)
where TEntity : class
{
var builder = SchemaBuilder.New()
return new ServiceCollection()
.AddDbContextPool<DatabaseContext<TEntity>>(
b => b.UseSqlite($"Data Source={Guid.NewGuid():N}.db"))
.AddGraphQL()
.AddQueryType(
c =>
{
Expand Down Expand Up @@ -64,10 +67,7 @@ protected IRequestExecutor CreateSchema<TEntity>(TEntity[] entities)
}
})
.UsePaging<ObjectType<TEntity>>(
options: new()
{
IncludeTotalCount = true,
});
options: new() { IncludeTotalCount = true, });

c.Field("root1")
.Resolve(
Expand All @@ -94,17 +94,8 @@ protected IRequestExecutor CreateSchema<TEntity>(TEntity[] entities)
}
}
});
});

var schema = builder.Create();

return new ServiceCollection()
.Configure<RequestExecutorSetup>(
Schema.DefaultName,
o => o.Schema = schema)
.AddDbContextPool<DatabaseContext<TEntity>>(
b => b.UseSqlite($"Data Source={Guid.NewGuid():N}.db"))
.AddGraphQL()
})
.AddQueryableCursorPagingProvider(inlineTotalCount: true)
.UseDefaultPipeline()
.Services
.BuildServiceProvider()
Expand Down
Loading

0 comments on commit 2d9d70e

Please sign in to comment.