diff --git a/src/Umbraco.Core/Services/IContentService.cs b/src/Umbraco.Core/Services/IContentService.cs index c6efe926c2..c609a7493d 100644 --- a/src/Umbraco.Core/Services/IContentService.cs +++ b/src/Umbraco.Core/Services/IContentService.cs @@ -78,17 +78,6 @@ namespace Umbraco.Core.Services /// IEnumerable GetByIds(IEnumerable ids); - /// - /// Gets paged documents of a content content - /// - /// The page number. - /// The page number. - /// The page size. - /// Total number of documents. - /// Search text filter. - /// Ordering infos. - IEnumerable GetPagedOfType(int contentType, long pageIndex, int pageSize, out long totalRecords, IQuery filter = null, Ordering ordering = null); - /// /// Gets documents at a given level. /// @@ -193,7 +182,7 @@ namespace Umbraco.Core.Services /// The ordering direction. /// Search text filter. IEnumerable GetPagedDescendants(int id, long pageIndex, int pageSize, out long totalRecords, - string orderBy = "Path", Direction orderDirection = Direction.Ascending, string filter = ""); + string filter = null, Ordering ordering = null); /// /// Gets descendant documents of a given parent. @@ -207,7 +196,31 @@ namespace Umbraco.Core.Services /// A flag indicating whether the ordering field is a system field. /// Query filter. IEnumerable GetPagedDescendants(int id, long pageIndex, int pageSize, out long totalRecords, - string orderBy, Direction orderDirection, bool orderBySystemField, IQuery filter); + IQuery filter, Ordering ordering = null); + + /// + /// Gets paged documents of a content content + /// + /// The page number. + /// The page number. + /// The page size. + /// Total number of documents. + /// Search text filter. + /// Ordering infos. + IEnumerable GetPagedOfType(int contentTypeId, long pageIndex, int pageSize, out long totalRecords, + IQuery filter, Ordering ordering = null); + + /// + /// Gets paged documents for specified content types + /// + /// The page number. + /// The page number. + /// The page size. + /// Total number of documents. + /// Search text filter. + /// Ordering infos. + IEnumerable GetPagedOfTypes(int[] contentTypeIds, long pageIndex, int pageSize, out long totalRecords, + IQuery filter, Ordering ordering = null); /// /// Counts documents of a given document type. diff --git a/src/Umbraco.Core/Services/Implement/ContentService.cs b/src/Umbraco.Core/Services/Implement/ContentService.cs index 2c8e38777b..1c3f5203f9 100644 --- a/src/Umbraco.Core/Services/Implement/ContentService.cs +++ b/src/Umbraco.Core/Services/Implement/ContentService.cs @@ -405,6 +405,7 @@ namespace Umbraco.Core.Services.Implement } } + /// public IEnumerable GetPagedOfType(int contentTypeId, long pageIndex, int pageSize, out long totalRecords, IQuery filter, Ordering ordering = null) { if(pageIndex < 0) throw new ArgumentOutOfRangeException(nameof(pageIndex)); @@ -422,6 +423,24 @@ namespace Umbraco.Core.Services.Implement } } + /// + public IEnumerable GetPagedOfTypes(int[] contentTypeIds, long pageIndex, int pageSize, out long totalRecords, IQuery filter, Ordering ordering = null) + { + if (pageIndex < 0) throw new ArgumentOutOfRangeException(nameof(pageIndex)); + if (pageSize <= 0) throw new ArgumentOutOfRangeException(nameof(pageSize)); + + if (ordering == null) + ordering = Ordering.By("sortOrder"); + + using (var scope = ScopeProvider.CreateScope(autoComplete: true)) + { + scope.ReadLock(Constants.Locks.ContentTree); + return _documentRepository.GetPage( + Query().Where(x => contentTypeIds.Contains(x.ContentTypeId)), + pageIndex, pageSize, out totalRecords, filter, ordering); + } + } + /// /// Gets a collection of objects by Level /// @@ -574,18 +593,24 @@ namespace Umbraco.Core.Services.Implement } /// - public IEnumerable GetPagedDescendants(int id, long pageIndex, int pageSize, out long totalChildren, string orderBy = "Path", Direction orderDirection = Direction.Ascending, string filter = "") + public IEnumerable GetPagedDescendants(int id, long pageIndex, int pageSize, out long totalChildren, + string filter = null, Ordering ordering = null) + //string orderBy = "Path", Direction orderDirection = Direction.Ascending, string filter = "") { var filterQuery = filter.IsNullOrWhiteSpace() ? null : Query().Where(x => x.Name.Contains(filter)); - return GetPagedDescendants(id, pageIndex, pageSize, out totalChildren, orderBy, orderDirection, true, filterQuery); + return GetPagedDescendants(id, pageIndex, pageSize, out totalChildren, filterQuery, ordering); } /// - public IEnumerable GetPagedDescendants(int id, long pageIndex, int pageSize, out long totalChildren, string orderBy, Direction orderDirection, bool orderBySystemField, IQuery filter) + public IEnumerable GetPagedDescendants(int id, long pageIndex, int pageSize, out long totalChildren, + IQuery filter, Ordering ordering = null) { + if (ordering == null) + ordering = Ordering.By("Path"); + using (var scope = ScopeProvider.CreateScope(autoComplete: true)) { scope.ReadLock(Constants.Locks.ContentTree); @@ -599,22 +624,26 @@ namespace Umbraco.Core.Services.Implement totalChildren = 0; return Enumerable.Empty(); } - return GetPagedDescendantsLocked(contentPath[0].Path, pageIndex, pageSize, out totalChildren, orderBy, orderDirection, orderBySystemField, filter); + return GetPagedDescendantsLocked(contentPath[0].Path, pageIndex, pageSize, out totalChildren, filter, ordering); } - return GetPagedDescendantsLocked(null, pageIndex, pageSize, out totalChildren, orderBy, orderDirection, orderBySystemField, filter); + return GetPagedDescendantsLocked(null, pageIndex, pageSize, out totalChildren, filter, ordering); } } - private IEnumerable GetPagedDescendantsLocked(string contentPath, long pageIndex, int pageSize, out long totalChildren, string orderBy, Direction orderDirection, bool orderBySystemField, IQuery filter) + private IEnumerable GetPagedDescendantsLocked(string contentPath, long pageIndex, int pageSize, out long totalChildren, + IQuery filter, Ordering ordering) { if (pageIndex < 0) throw new ArgumentOutOfRangeException(nameof(pageIndex)); if (pageSize <= 0) throw new ArgumentOutOfRangeException(nameof(pageSize)); + if (filter == null) throw new ArgumentNullException(nameof(filter)); + + if (ordering == null) throw new ArgumentNullException(nameof(ordering)); var query = Query(); if (!contentPath.IsNullOrWhiteSpace()) query.Where(x => x.Path.SqlStartsWith($"{contentPath},", TextColumnType.NVarchar)); - return _documentRepository.GetPage(query, pageIndex, pageSize, out totalChildren, filter, Ordering.By(orderBy, orderDirection, isCustomField: !orderBySystemField)); + return _documentRepository.GetPage(query, pageIndex, pageSize, out totalChildren, filter, ordering); } /// @@ -1412,7 +1441,7 @@ namespace Umbraco.Core.Services.Implement while (page * pageSize < total) { //get descendants - ordered from deepest to shallowest - var descendants = GetPagedDescendants(content.Id, page, pageSize, out total, "Path", Direction.Descending); + var descendants = GetPagedDescendants(content.Id, page, pageSize, out total, ordering: Ordering.By("Path", Direction.Descending)); foreach (var c in descendants) DoDelete(c); } @@ -1645,7 +1674,7 @@ namespace Umbraco.Core.Services.Implement var total = long.MaxValue; while(page * pageSize < total) { - var descendants = GetPagedDescendantsLocked(originalPath, page++, pageSize, out total, "Path", Direction.Ascending, true, null); + var descendants = GetPagedDescendantsLocked(originalPath, page++, pageSize, out total, null, Ordering.By("Path", Direction.Ascending)); foreach (var descendant in descendants) { moves.Add(Tuple.Create(descendant, descendant.Path)); // capture original path diff --git a/src/Umbraco.Examine/UmbracoContentIndexer.cs b/src/Umbraco.Examine/UmbracoContentIndexer.cs index 94982c8591..fab9f226a4 100644 --- a/src/Umbraco.Examine/UmbracoContentIndexer.cs +++ b/src/Umbraco.Examine/UmbracoContentIndexer.cs @@ -258,7 +258,8 @@ namespace Umbraco.Examine else { //add the published filter - descendants = ContentService.GetPagedDescendants(contentParentId, pageIndex, pageSize, out total, "Path", Direction.Ascending, true, _publishedQuery); + descendants = ContentService.GetPagedDescendants(contentParentId, pageIndex, pageSize, out total, + _publishedQuery, Ordering.By("Path", Direction.Ascending)); } //if specific types are declared we need to post filter them diff --git a/src/Umbraco.Tests/Services/ContentServiceTests.cs b/src/Umbraco.Tests/Services/ContentServiceTests.cs index 1f99bd1127..c2b1cdc52d 100644 --- a/src/Umbraco.Tests/Services/ContentServiceTests.cs +++ b/src/Umbraco.Tests/Services/ContentServiceTests.cs @@ -1718,7 +1718,7 @@ namespace Umbraco.Tests.Services // Act contentService.DeleteOfType(contentType.Id); var rootContent = contentService.GetRootContent(); - var contents = contentService.GetPagedOfType(contentType.Id, 0, int.MaxValue, out var _); + var contents = contentService.GetPagedOfType(contentType.Id, 0, int.MaxValue, out var _, null); // Assert Assert.That(rootContent.Any(), Is.False); diff --git a/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs b/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs index dedf04488c..b197cea5bc 100644 --- a/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs +++ b/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs @@ -71,11 +71,11 @@ namespace Umbraco.Tests.UmbracoExamine contentService = Mock.Of( x => x.GetPagedDescendants( - It.IsAny(), It.IsAny(), It.IsAny(), out longTotalRecs, It.IsAny(), It.IsAny(), It.IsAny()) + It.IsAny(), It.IsAny(), It.IsAny(), out longTotalRecs, It.IsAny(), It.IsAny()) == allRecs && x.GetPagedDescendants( - It.IsAny(), It.IsAny(), It.IsAny(), out longTotalRecs, It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny>()) + It.IsAny(), It.IsAny(), It.IsAny(), out longTotalRecs, It.IsAny>(), It.IsAny()) == allRecs); } diff --git a/src/Umbraco.Tests/UmbracoExamine/SearchTests.cs b/src/Umbraco.Tests/UmbracoExamine/SearchTests.cs index 2d440b8453..768d1c735c 100644 --- a/src/Umbraco.Tests/UmbracoExamine/SearchTests.cs +++ b/src/Umbraco.Tests/UmbracoExamine/SearchTests.cs @@ -49,7 +49,7 @@ namespace Umbraco.Tests.UmbracoExamine .ToArray(); var contentService = Mock.Of( x => x.GetPagedDescendants( - It.IsAny(), It.IsAny(), It.IsAny(), out totalRecs, It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny>()) + It.IsAny(), It.IsAny(), It.IsAny(), out totalRecs, It.IsAny>(), It.IsAny()) == allRecs); diff --git a/src/Umbraco.Web/Search/ExamineComponent.cs b/src/Umbraco.Web/Search/ExamineComponent.cs index 05be969f92..b5cfac7e99 100644 --- a/src/Umbraco.Web/Search/ExamineComponent.cs +++ b/src/Umbraco.Web/Search/ExamineComponent.cs @@ -26,6 +26,7 @@ using Umbraco.Web.Cache; using Umbraco.Web.Composing; using Umbraco.Web.PropertyEditors; using Umbraco.Examine; +using Umbraco.Core.Persistence.DatabaseModelDefinitions; namespace Umbraco.Web.Search { @@ -351,21 +352,42 @@ namespace Umbraco.Web.Search const int pageSize = 500; - //Re-index all content of these types - foreach(var id in refreshedIds.Concat(otherIds).Distinct()) - { + if (refreshedIds.Count > 0 || otherIds.Count > 0) + { var page = 0; var total = long.MaxValue; while (page * pageSize < total) { - var contentToRefresh = _services.ContentService.GetPagedOfType(id, page++, pageSize, out total); - foreach(var c in contentToRefresh) + var contentToRefresh = _services.ContentService.GetPagedOfTypes( + //Re-index all content of these types + refreshedIds.Concat(otherIds).Distinct().ToArray(), + page++, pageSize, out total, null, + //order by shallowest to deepest, this allows us to check it's published state without checking every item + Ordering.By("Path", Direction.Ascending)); + + //track which Ids have their paths are published + var publishChecked = new Dictionary(); + + foreach (var c in contentToRefresh) { - //TODO: We might have to order by Path ascending or something since we're going to need to check - // contentService.IsPathPublished(content) but we don't want to make that check for every content item IContent published = null; - if (c.Published && contentService.IsPathPublished(c)) - published = c; + if (c.Published) + { + if (publishChecked.TryGetValue(c.ParentId, out var isPublished)) + { + //if the parent's published path has already been verified then this is published + if (isPublished) + published = c; + } + else + { + //nothing by parent id, so query the service and cache the result for the next child to check against + isPublished = contentService.IsPathPublished(c); + publishChecked[c.Id] = isPublished; + if (isPublished) + published = c; + } + } ReIndexForContent(c, published); } @@ -460,7 +482,10 @@ namespace Umbraco.Web.Search var total = long.MaxValue; while(page * pageSize < total) { - var descendants = contentService.GetPagedDescendants(content.Id, page++, pageSize, out total); + var descendants = contentService.GetPagedDescendants(content.Id, page++, pageSize, out total, + //order by shallowest to deepest, this allows us to check it's published state without checking every item + ordering: Ordering.By("Path", Direction.Ascending)); + foreach (var descendant in descendants) { published = null;