Skip to content

Commit

Permalink
Work in progress for RepoDb offset paging, but BatchQuery is not work…
Browse files Browse the repository at this point in the history
…ing as originally thought, will have to re-factor to implement a Skip/Take query manually!
  • Loading branch information
cajuncoding committed Apr 23, 2021
1 parent 4d9b220 commit fc8fbcd
Show file tree
Hide file tree
Showing 7 changed files with 84 additions and 20 deletions.
7 changes: 6 additions & 1 deletion GraphQL.RepoDb.SqlServer/GraphQLRepoDbMapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,12 @@ public RepoDbOffsetPagingParams GetOffsetPagingParameters()
return null;
}

return RepoDbOffsetPagingParams.FromSkipTake(graphQLPagingArgs.Skip, graphQLPagingArgs.Take);

return RepoDbOffsetPagingParams.FromSkipTake(
graphQLPagingArgs.Skip,
graphQLPagingArgs.Take,
this.GraphQLParamsContext.IsTotalCountRequested
);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@ public interface IRepoDbOffsetPagingParams
{
int Page { get; }
int RowsPerBatch { get; }
bool IsTotalCountEnabled { get; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
using System.Threading.Tasks;
using RepoDb.Interfaces;

namespace RepoDb.CursorPagination
namespace RepoDb.OffsetPagination
{
public static class BaseRepositoryOffsetPaginationCustomExtensions
{
Expand All @@ -25,6 +25,7 @@ public static class BaseRepositoryOffsetPaginationCustomExtensions
/// <param name="where"></param>
/// <param name="page"></param>
/// <param name="rowsPerBatch"></param>
/// <param name="fetchTotalCount"></param>
/// <param name="tableName"></param>
/// <param name="hints"></param>
/// <param name="fields"></param>
Expand All @@ -40,6 +41,7 @@ public static async Task<OffsetPageResults<TEntity>> GraphQLBatchOffsetPagingQue
Expression<Func<TEntity, bool>> where,
int? page = null,
int? rowsPerBatch = null,
bool fetchTotalCount = false,
string tableName = null,
string hints = null,
IEnumerable<Field> fields = null,
Expand All @@ -55,6 +57,7 @@ public static async Task<OffsetPageResults<TEntity>> GraphQLBatchOffsetPagingQue
return await baseRepo.GraphQLBatchOffsetPagingQueryAsync<TEntity, TDbConnection>(
page: page,
rowsPerBatch: rowsPerBatch,
fetchTotalCount: fetchTotalCount,
orderBy: orderBy,
where: where != null ? QueryGroup.Parse<TEntity>(where) : (QueryGroup)null,
hints: hints,
Expand All @@ -78,6 +81,7 @@ public static async Task<OffsetPageResults<TEntity>> GraphQLBatchOffsetPagingQue
/// <param name="page"></param>
/// <param name="rowsPerBatch"></param>
/// <param name="tableName"></param>
/// <param name="fetchTotalCount"></param>
/// <param name="hints"></param>
/// <param name="fields"></param>
/// <param name="commandTimeout"></param>
Expand All @@ -91,6 +95,7 @@ public static async Task<OffsetPageResults<TEntity>> GraphQLBatchOffsetPagingQue
QueryGroup where = null,
int? page = null,
int? rowsPerBatch = null,
bool fetchTotalCount = false,
string hints = null,
IEnumerable<Field> fields = null,
string tableName = null,
Expand All @@ -111,6 +116,7 @@ public static async Task<OffsetPageResults<TEntity>> GraphQLBatchOffsetPagingQue
var cursorPageResult = await connection.GraphQLBatchOffsetPagingQueryAsync<TEntity>(
page: page,
rowsPerBatch: rowsPerBatch,
fetchTotalCount: fetchTotalCount,
orderBy: orderBy,
where: where,
hints: hints,
Expand Down Expand Up @@ -145,6 +151,7 @@ public static async Task<OffsetPageResults<TEntity>> GraphQLBatchOffsetPagingQue
/// <param name="where"></param>
/// <param name="page"></param>
/// <param name="rowsPerBatch"></param>
/// <param name="fetchTotalCount"></param>
/// <param name="hints"></param>
/// <param name="fields"></param>
/// <param name="commandTimeout"></param>
Expand All @@ -159,6 +166,7 @@ public static async Task<OffsetPageResults<TEntity>> GraphQLBatchOffsetPagingQue
Expression<Func<TEntity, bool>> where,
int? page = null,
int? rowsPerBatch = null,
bool fetchTotalCount = false,
string hints = null,
IEnumerable<Field> fields = null,
int? commandTimeout = null,
Expand All @@ -172,6 +180,7 @@ public static async Task<OffsetPageResults<TEntity>> GraphQLBatchOffsetPagingQue
return await dbConnection.GraphQLBatchOffsetPagingQueryAsync<TEntity>(
page: page,
rowsPerBatch: rowsPerBatch,
fetchTotalCount: fetchTotalCount,
orderBy: orderBy,
where: where != null ? QueryGroup.Parse<TEntity>(where) : (QueryGroup)null,
hints: hints,
Expand All @@ -193,6 +202,7 @@ public static async Task<OffsetPageResults<TEntity>> GraphQLBatchOffsetPagingQue
/// <param name="page"></param>
/// <param name="rowsPerBatch"></param>
/// <param name="tableName"></param>
/// <param name="fetchTotalCount"></param>
/// <param name="hints"></param>
/// <param name="fields"></param>
/// <param name="commandTimeout"></param>
Expand All @@ -206,6 +216,7 @@ public static async Task<OffsetPageResults<TEntity>> GraphQLBatchOffsetPagingQue
QueryGroup where = null,
int? page = null,
int? rowsPerBatch = null,
bool fetchTotalCount = false,
string hints = null,
IEnumerable<Field> fields = null,
string tableName = null,
Expand Down Expand Up @@ -242,10 +253,14 @@ public static async Task<OffsetPageResults<TEntity>> GraphQLBatchOffsetPagingQue
? RepoDbQueryGroupProxy.GetMappedParamsObject<TEntity>(where)
: null;

var batchResults = await dbConnection.BatchQueryAsync<TEntity>(
tableName: tableName,
//We increment Rows count to actually be fetched by One to help determine if there is any data after the current page...
var rowsPerPage = rowsPerBatch ?? int.MaxValue;
var rowsToFetch = rowsPerPage < int.MaxValue ? rowsPerPage + 1 : rowsPerPage;

var batchResults = (await dbConnection.BatchQueryAsync<TEntity>(
tableName: dbTableName,
page: page ?? 0,
rowsPerBatch: rowsPerBatch ?? int.MaxValue,
rowsPerBatch: rowsToFetch,
fields: validSelectFields,
orderBy: orderBy,
where: whereParams,
Expand All @@ -254,10 +269,34 @@ public static async Task<OffsetPageResults<TEntity>> GraphQLBatchOffsetPagingQue
transaction: transaction,
trace: trace,
cancellationToken: cancellationToken
).ConfigureAwait(false);
).ConfigureAwait(false))
?.ToList() ?? new List<TEntity>();

//If specified we need to get the Total Count...
int totalCount = 0;
if (fetchTotalCount)
{
var repoDbTotalCount = await dbConnection.CountAsync(
tableName: dbTableName,
where: whereParams,
hints: hints,
commandTimeout: commandTimeout,
transaction: transaction,
trace: trace,
cancellationToken: cancellationToken
).ConfigureAwait(false);

totalCount = Convert.ToInt32(repoDbTotalCount);
}

//TODO: Implement Logic to determine HasNextPage, HasPreviousPage, and get Total Count or Not!
var offsetPageResults = new OffsetPageResults<TEntity>(batchResults, true, true, 0);
var hasPreviousPage = page > 0;
var hasNextPage = batchResults.Count > rowsPerPage;

//Trim the results to the exact page size (removing potential additional item).
var pageResults = batchResults.Take(rowsPerPage);

var offsetPageResults = new OffsetPageResults<TEntity>(pageResults, hasNextPage, hasPreviousPage, totalCount);
return offsetPageResults;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,15 @@ public class RepoDbOffsetPagingParams : IRepoDbOffsetPagingParams
/// </summary>
/// <param name="rowsPerBatch">Is Optional and may be null to get all results; will default to getting all data (int.MaxValue) if null or is not a valid positive value.</param>
/// <param name="page">Is Optional and will default to the first page (zero-based) if null or is not a valid positive value.</param>
public RepoDbOffsetPagingParams(int? rowsPerBatch = null, int? page = null)
/// <param name="fetchTotalCount">Enable the retrieval of the Total Count; for OffsetPaging this is optional and enabling it may impact performance.</param>
public RepoDbOffsetPagingParams(int? rowsPerBatch = null, int? page = null, bool fetchTotalCount = false)
{
if (rowsPerBatch.HasValue && rowsPerBatch <= 0)
throw new ArgumentException("A valid number of rows per batch must be specified (>0).", nameof(rowsPerBatch));

this.RowsPerBatch = rowsPerBatch.HasValue && rowsPerBatch > 0 ? (int)rowsPerBatch : int.MaxValue;
this.Page = page.HasValue && page > 0 ? (int)page : 0;
this.IsTotalCountEnabled = fetchTotalCount;
}

/// <summary>
Expand All @@ -32,8 +34,9 @@ public RepoDbOffsetPagingParams(int? rowsPerBatch = null, int? page = null)
/// </summary>
/// <param name="skip">Is Optional and may be null to get all results; will default to skipping none (0) if null or is not a valid positive value.</param>
/// <param name="take">Is Optional and may be null to get all results; will default to getting all data (int.MaxValue) if null or is not a valid positive value.</param>
/// <param name="fetchTotalCount">Enable the retrieval of the Total Count; for OffsetPaging this is optional and enabling it may impact performance.</param>
/// <returns></returns>
public static RepoDbOffsetPagingParams FromSkipTake(int? skip = null, int? take = null)
public static RepoDbOffsetPagingParams FromSkipTake(int? skip = null, int? take = null, bool fetchTotalCount = false)
{
if (take.HasValue && take <= 0)
throw new ArgumentException("A valid number of items to take (rows-per-page) must be specified (e.g. greater than 0).", nameof(take));
Expand All @@ -48,10 +51,11 @@ public static RepoDbOffsetPagingParams FromSkipTake(int? skip = null, int? take
var skipOver = Math.Max((skip ?? 0), 0);
var page = (int)(skipOver / rowsPerBatch);

return new RepoDbOffsetPagingParams(rowsPerBatch, page);
return new RepoDbOffsetPagingParams(rowsPerBatch, page, fetchTotalCount);
}

public int Page { get; }
public int RowsPerBatch { get; }
public bool IsTotalCountEnabled { get; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ [GraphQLParams] IParamsContext graphQLParams
// down to the Repository (and underlying Database) layer.
var charactersSlice = await repository.GetCursorPagedCharactersAsync(
repoDbParams.GetSelectFields(),
repoDbParams.GetSortOrderFields() ?? OrderField.Parse(new { Name = Order.Ascending }),
repoDbParams.GetSortOrderFields(),
repoDbParams.GetCursorPagingParameters()
);

Expand Down Expand Up @@ -91,7 +91,7 @@ [GraphQLParams] IParamsContext graphQLParams
// down to the Repository (and underlying Database) layer.
var charactersPage = await repository.GetOffsetPagedCharactersAsync(
repoDbParams.GetSelectFields(),
repoDbParams.GetSortOrderFields() ?? OrderField.Parse(new { Name = Order.Ascending }),
repoDbParams.GetSortOrderFields(),
repoDbParams.GetOffsetPagingParameters()
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,20 @@
using HotChocolate.PreProcessingExtensions.Pagination;
using Microsoft.Data.SqlClient;
using RepoDb;
using RepoDb.CursorPagination;
using RepoDb.Enumerations;
using RepoDb.CursorPagination;
using RepoDb.OffsetPagination;
using StarWars.Characters;
using StarWars.Characters.DbModels;

namespace StarWars.Repositories
{
public class CharacterRepository : BaseRepository<ICharacter, SqlConnection>, ICharacterRepository
{
public static class TableNames
public static readonly IReadOnlyList<OrderField> DefaultCharacterSortFields = new List<OrderField>()
{
public const string StarWarsCharacters = "StarWarsCharacters";
}
OrderField.Ascending<ICharacter>(c => c.Id)
}.AsReadOnly();

public CharacterRepository(string connectionString)
: base(connectionString)
Expand All @@ -38,7 +39,7 @@ IEnumerable<OrderField> sortFields
var results = await sqlConn.QueryAsync<CharacterDbModel>(
where: c => c.Id >= 1000 && c.Id <=2999,
fields: selectFields,
orderBy: sortFields
orderBy: sortFields ?? DefaultCharacterSortFields
);

var mappedResults = MapDbModelsToCharacterModels(results);
Expand All @@ -56,7 +57,7 @@ IRepoDbCursorPagingParams pagingParams

var pageSlice = await sqlConn.GraphQLBatchSliceQueryAsync<CharacterDbModel>(
fields: selectFields,
orderBy: sortFields,
orderBy: sortFields ?? DefaultCharacterSortFields,
afterCursor: pagingParams.AfterIndex!,
beforeCursor: pagingParams.BeforeIndex!,
firstTake: pagingParams.First,
Expand All @@ -80,7 +81,8 @@ IRepoDbOffsetPagingParams pagingParams
var offsetPageResults = await sqlConn.GraphQLBatchOffsetPagingQueryAsync<CharacterDbModel>(
page: pagingParams.Page,
rowsPerBatch: pagingParams.RowsPerBatch,
orderBy: sortFields,
fetchTotalCount: pagingParams.IsTotalCountEnabled,
orderBy: sortFields ?? DefaultCharacterSortFields,
fields: selectFields
);

Expand All @@ -97,7 +99,7 @@ IRepoDbCursorPagingParams pagingParams
await using var sqlConn = CreateConnection();

var pageSlice = await sqlConn.GraphQLBatchSliceQueryAsync<CharacterDbModel>(
orderBy: sortFields,
orderBy: sortFields ?? DefaultCharacterSortFields,
fields: selectFields,
where: c => c.Id >=1000 && c.Id <= 1999,
afterCursor: pagingParams.AfterIndex!,
Expand Down
13 changes: 13 additions & 0 deletions Temp/RepoDB Batch Offset Paging Tests.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
SELECT * FROM StarWarsCharacters ORDER BY Id ASC

DECLARE @page AS INT = 1;
DECLARE @rowsPerBatch AS INT = 8;

SELECT TOP((@page + 1)*@rowsPerBatch) ROW_NUMBER() OVER (ORDER BY Id ASC) AS [RowNumber], [Id], [Name] FROM [StarWarsCharacters] ORDER BY Id ASC;

--Modeled from RepoDB's BatchQuery logic (query builder)
WITH CTE AS (SELECT TOP((@page + 1)*@rowsPerBatch) ROW_NUMBER() OVER (ORDER BY Id ASC) AS [RowNumber], [Id], [Name] FROM [StarWarsCharacters] ORDER BY Id ASC)
SELECT [Id], [Name] FROM CTE WHERE ([RowNumber] BETWEEN ((@page * @rowsPerBatch) + 1) AND ((@page + 1) * @rowsPerBatch));

SELECT
BetweenSql = 'Between ' + CONVERT(VARCHAR(5), ((@page * @rowsPerBatch) + 1)) + ' and ' + CONVERT(VARCHAR(5), ((@page + 1) * @rowsPerBatch));

0 comments on commit fc8fbcd

Please sign in to comment.