From dc32138006d5d6c7bdb64e5ef96bed7a6c844614 Mon Sep 17 00:00:00 2001 From: AndyButland Date: Tue, 12 Aug 2014 08:15:43 +0100 Subject: [PATCH] Set up database level paging for retrieving child content for list view --- src/Umbraco.Core/Persistence/PetaPoco.cs | 5 ++ .../Repositories/ContentRepository.cs | 67 ++++++++++++++++++- .../Interfaces/IContentRepository.cs | 13 ++++ src/Umbraco.Core/Services/ContentService.cs | 24 +++++++ src/Umbraco.Core/Services/IContentService.cs | 14 ++++ src/Umbraco.Web/Editors/ContentController.cs | 50 +++++--------- 6 files changed, 137 insertions(+), 36 deletions(-) diff --git a/src/Umbraco.Core/Persistence/PetaPoco.cs b/src/Umbraco.Core/Persistence/PetaPoco.cs index d8507ac681..41abe7640e 100644 --- a/src/Umbraco.Core/Persistence/PetaPoco.cs +++ b/src/Umbraco.Core/Persistence/PetaPoco.cs @@ -2309,6 +2309,11 @@ namespace Umbraco.Core.Persistence return Append(new Sql("ORDER BY " + String.Join(", ", (from x in columns select x.ToString()).ToArray()))); } + public Sql OrderByDescending(params object[] columns) + { + return Append(new Sql("ORDER BY " + String.Join(", ", (from x in columns select x.ToString() + " DESC").ToArray()))); + } + public Sql Select(params object[] columns) { return Append(new Sql("SELECT " + String.Join(", ", (from x in columns select x.ToString()).ToArray()))); diff --git a/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs index c1633daa75..ed1a87b2de 100644 --- a/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs @@ -3,8 +3,10 @@ using System.Collections.Generic; using System.Data; using System.Globalization; using System.Linq; +using System.Linq.Expressions; using System.Xml.Linq; using Umbraco.Core.Configuration; +using Umbraco.Core.Dynamics; using Umbraco.Core.IO; using Umbraco.Core.Models; using Umbraco.Core.Models.EntityBase; @@ -636,7 +638,70 @@ namespace Umbraco.Core.Persistence.Repositories _contentPreviewRepository.AddOrUpdate(new ContentPreviewEntity(previewExists, content, xml)); } - + + /// + /// Gets paged content results + /// + /// Query to excute + /// Page number + /// Page size + /// Total records query would return without paging + /// Field to order by + /// Direction to order by + /// An Enumerable list of objects + public IEnumerable GetPagedResultsByQuery(IQuery query, int pageNumber, int pageSize, out int totalRecords, + string orderBy, Direction orderDirection) + { + // Get base query + var sqlClause = GetBaseQuery(false); + var translator = new SqlTranslator(sqlClause, query); + var sql = translator.Translate() + .Where(x => x.Newest); + + // Apply order by according to parameters + var orderByParams = new[] { orderBy }; + if (orderDirection == Direction.Ascending) + { + sql = sql.OrderBy(orderByParams); + } + else + { + sql = sql.OrderByDescending(orderByParams); + } + + // Note we can't do multi-page for several DTOs like we can multi-fetch and are doing in PerformGetByQuery, + // but actually given we are doing a Get on each one (again as in PerformGetByQuery), we only need the node Id + IEnumerable result; + var pagedResult = Database.Page(pageNumber, pageSize, sql.SQL.Replace("SELECT *", "SELECT cmsDocument.nodeId")); + totalRecords = Convert.ToInt32(pagedResult.TotalItems); + if (totalRecords > 0) + { + // Parse out node Ids and load content (we need the cast here in order to be able to call the IQueryable extension + // methods OrderBy or OrderByDescending) + var content = GetAll(pagedResult.Items + .DistinctBy(x => x.NodeId) + .Select(x => x.NodeId).ToArray()) + .Cast() + .AsQueryable(); + + // Now we need to ensure this result is also ordered by the same order by clause + if (orderDirection == Direction.Ascending) + { + result = content.OrderBy(orderBy); + } + else + { + result = content.OrderByDescending(orderBy); + } + } + else + { + result = Enumerable.Empty(); + } + + return result; + } + #endregion /// diff --git a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IContentRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IContentRepository.cs index 2fd0a5685e..46bcd7666f 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IContentRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IContentRepository.cs @@ -1,8 +1,10 @@ using System; using System.Collections.Generic; +using System.Linq.Expressions; using System.Xml.Linq; using Umbraco.Core.Models; using Umbraco.Core.Models.Membership; +using Umbraco.Core.Persistence.DatabaseModelDefinitions; using Umbraco.Core.Persistence.Querying; namespace Umbraco.Core.Persistence.Repositories @@ -66,5 +68,16 @@ namespace Umbraco.Core.Persistence.Repositories /// void AddOrUpdatePreviewXml(IContent content, Func xml); + /// + /// Gets paged content results + /// + /// Query to excute + /// Page number + /// Page size + /// Total records query would return without paging + /// Field to order by + /// Direction to order by + /// An Enumerable list of objects + IEnumerable GetPagedResultsByQuery(IQuery query, int pageNumber, int pageSize, out int totalRecords, string orderBy, Direction orderDirection); } } \ No newline at end of file diff --git a/src/Umbraco.Core/Services/ContentService.cs b/src/Umbraco.Core/Services/ContentService.cs index 311e68a482..27fd5c1f3d 100644 --- a/src/Umbraco.Core/Services/ContentService.cs +++ b/src/Umbraco.Core/Services/ContentService.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.Globalization; using System.Linq; +using System.Linq.Expressions; using System.Threading; using System.Xml.Linq; using Umbraco.Core.Auditing; @@ -13,6 +14,7 @@ using Umbraco.Core.Models.Membership; using Umbraco.Core.Models.Rdbms; using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.Caching; +using Umbraco.Core.Persistence.DatabaseModelDefinitions; using Umbraco.Core.Persistence.Querying; using Umbraco.Core.Persistence.Repositories; using Umbraco.Core.Persistence.SqlSyntax; @@ -473,6 +475,28 @@ namespace Umbraco.Core.Services } } + /// + /// Gets a collection of objects by Parent Id + /// + /// Id of the Parent to retrieve Children from + /// Page number + /// Page size + /// Total records query would return without paging + /// Field to order by + /// Direction to order by + /// An Enumerable list of objects + public IEnumerable GetPagedChildren(int id, int pageNumber, int pageSize, out int totalChildren, + string orderBy, Direction orderDirection) + { + using (var repository = _repositoryFactory.CreateContentRepository(_uowProvider.GetUnitOfWork())) + { + var query = Query.Builder.Where(x => x.ParentId == id); + var contents = repository.GetPagedResultsByQuery(query, pageNumber, pageSize, out totalChildren, orderBy, orderDirection); + + return contents; + } + } + /// /// Gets a collection of objects by its name or partial name /// diff --git a/src/Umbraco.Core/Services/IContentService.cs b/src/Umbraco.Core/Services/IContentService.cs index 76964e8e9a..efb045f6f6 100644 --- a/src/Umbraco.Core/Services/IContentService.cs +++ b/src/Umbraco.Core/Services/IContentService.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using Umbraco.Core.Models; using Umbraco.Core.Models.Membership; +using Umbraco.Core.Persistence.DatabaseModelDefinitions; using Umbraco.Core.Publishing; namespace Umbraco.Core.Services @@ -108,6 +109,19 @@ namespace Umbraco.Core.Services /// An Enumerable list of objects IEnumerable GetChildren(int id); + /// + /// Gets a collection of objects by Parent Id + /// + /// Id of the Parent to retrieve Children from + /// Page number + /// Page size + /// Total records query would return without paging + /// Field to order by + /// Direction to order by + /// An Enumerable list of objects + IEnumerable GetPagedChildren(int id, int pageNumber, int pageSize, out int totalChildren, + string orderBy, Direction orderDirection); + /// /// Gets a collection of an objects versions by its Id /// diff --git a/src/Umbraco.Web/Editors/ContentController.cs b/src/Umbraco.Web/Editors/ContentController.cs index d2a023ee98..49a0f12e1f 100644 --- a/src/Umbraco.Web/Editors/ContentController.cs +++ b/src/Umbraco.Web/Editors/ContentController.cs @@ -157,47 +157,27 @@ namespace Umbraco.Web.Editors Direction orderDirection = Direction.Ascending, string filter = "") { - //TODO: This will be horribly inefficient for paging! This is because our datasource/repository - // doesn't support paging at the SQL level... and it'll be pretty interesting to try to make that work. + //TODO: move this filter to repo level as needs to be included in paging + //if (!string.IsNullOrEmpty(filter)) + //{ + // filter = filter.ToLower(); + // result = result.Where(x => x.Name.InvariantContains(filter)); + //} - //PP: could we in 7.0.1+ migrate this to the internal examine index instead of using the content service? - - var children = Services.ContentService.GetChildren(id).ToArray(); - var totalChildren = children.Length; + int totalChildren; + var children = Services.ContentService.GetPagedChildren(id, pageNumber, pageSize, out totalChildren, orderBy, orderDirection).ToArray(); if (totalChildren == 0) + { return new PagedResult>(0, 0, 0); - - var result = children - .Select(Mapper.Map>) - .AsQueryable(); - - //TODO: This is a rudimentry filter - should use the logic found in the EntityService filter (dynamic linq) instead - if (!string.IsNullOrEmpty(filter)) - { - filter = filter.ToLower(); - result = result.Where(x => x.Name.InvariantContains(filter)); } - var orderedResult = orderDirection == Direction.Ascending - ? result.OrderBy(orderBy) - : result.OrderByDescending(orderBy); - - var pagedResult = new PagedResult>( - totalChildren, - pageNumber, - pageSize); - - if (pageNumber > 0 && pageSize > 0) - { - pagedResult.Items = orderedResult - .Skip(pagedResult.SkipSize) - .Take(pageSize); - } - else - { - pagedResult.Items = orderedResult; - } + var pagedResult = new PagedResult>( + totalChildren, + pageNumber, + pageSize); + pagedResult.Items = children + .Select(Mapper.Map>); return pagedResult; }