diff --git a/src/Umbraco.Core/Services/IMediaService.cs b/src/Umbraco.Core/Services/IMediaService.cs index 6fa211f4f3..0c6e528dee 100644 --- a/src/Umbraco.Core/Services/IMediaService.cs +++ b/src/Umbraco.Core/Services/IMediaService.cs @@ -137,11 +137,28 @@ namespace Umbraco.Core.Services IQuery filter, Ordering ordering = null); /// - /// Gets a collection of objects by the Id of the + /// Gets paged documents of a content content /// - /// Id of the - /// An Enumerable list of objects - IEnumerable GetMediaOfMediaType(int id); + /// 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); /// /// Gets a collection of objects, which reside at the first level / root diff --git a/src/Umbraco.Core/Services/Implement/MediaService.cs b/src/Umbraco.Core/Services/Implement/MediaService.cs index a584521c33..a69b39943b 100644 --- a/src/Umbraco.Core/Services/Implement/MediaService.cs +++ b/src/Umbraco.Core/Services/Implement/MediaService.cs @@ -364,18 +364,39 @@ namespace Umbraco.Core.Services.Implement } } - /// - /// Gets a collection of objects by the Id of the - /// - /// Id of the - /// An Enumerable list of objects - public IEnumerable GetMediaOfMediaType(int id) + /// + 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)); + 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.MediaTree); - var query = Query().Where(x => x.ContentTypeId == id); - return _mediaRepository.Get(query); + scope.ReadLock(Constants.Locks.ContentTree); + return _mediaRepository.GetPage( + Query().Where(x => x.ContentTypeId == contentTypeId), + pageIndex, pageSize, out totalRecords, filter, ordering); + } + } + + /// + 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 _mediaRepository.GetPage( + Query().Where(x => contentTypeIds.Contains(x.ContentTypeId)), + pageIndex, pageSize, out totalRecords, filter, ordering); } } diff --git a/src/Umbraco.Core/Services/Implement/MemberService.cs b/src/Umbraco.Core/Services/Implement/MemberService.cs index 3fd714f974..5a644cfec1 100644 --- a/src/Umbraco.Core/Services/Implement/MemberService.cs +++ b/src/Umbraco.Core/Services/Implement/MemberService.cs @@ -393,12 +393,14 @@ namespace Umbraco.Core.Services.Implement // fixme get rid of string filter? - public IEnumerable GetAll(long pageIndex, int pageSize, out long totalRecords, string orderBy, Direction orderDirection, string memberTypeAlias = null, string filter = "") + public IEnumerable GetAll(long pageIndex, int pageSize, out long totalRecords, + string orderBy, Direction orderDirection, string memberTypeAlias = null, string filter = "") { return GetAll(pageIndex, pageSize, out totalRecords, orderBy, orderDirection, true, memberTypeAlias, filter); } - public IEnumerable GetAll(long pageIndex, int pageSize, out long totalRecords, string orderBy, Direction orderDirection, bool orderBySystemField, string memberTypeAlias, string filter) + public IEnumerable GetAll(long pageIndex, int pageSize, out long totalRecords, + string orderBy, Direction orderDirection, bool orderBySystemField, string memberTypeAlias, string filter) { using (var scope = ScopeProvider.CreateScope(autoComplete: true)) { diff --git a/src/Umbraco.Web/Search/ExamineComponent.cs b/src/Umbraco.Web/Search/ExamineComponent.cs index b5cfac7e99..7a036ef712 100644 --- a/src/Umbraco.Web/Search/ExamineComponent.cs +++ b/src/Umbraco.Web/Search/ExamineComponent.cs @@ -333,92 +333,155 @@ namespace Umbraco.Web.Search if (args.MessageType != MessageType.RefreshByPayload) throw new NotSupportedException(); - - var contentService = _services.ContentService; - - var removedIds = new List(); - var refreshedIds = new List(); - var otherIds = new List(); - + + var changedIds = new Dictionary removedIds, List refreshedIds, List otherIds)>(); + foreach (var payload in (ContentTypeCacheRefresher.JsonPayload[])args.MessageObject) { + if (!changedIds.TryGetValue(payload.ItemType, out var idLists)) + { + idLists = (removedIds: new List(), refreshedIds: new List(), otherIds: new List()); + changedIds.Add(payload.ItemType, idLists); + } + if (payload.ChangeTypes.HasType(ContentTypeChangeTypes.Remove)) - removedIds.Add(payload.Id); + idLists.removedIds.Add(payload.Id); else if (payload.ChangeTypes.HasType(ContentTypeChangeTypes.RefreshMain)) - refreshedIds.Add(payload.Id); + idLists.refreshedIds.Add(payload.Id); else if (payload.ChangeTypes.HasType(ContentTypeChangeTypes.RefreshOther)) - otherIds.Add(payload.Id); + idLists.otherIds.Add(payload.Id); } const int pageSize = 500; - if (refreshedIds.Count > 0 || otherIds.Count > 0) - { + foreach(var ci in changedIds) + { + if (ci.Value.refreshedIds.Count > 0 || ci.Value.otherIds.Count > 0) + { + switch(ci.Key) + { + case var itemType when itemType == typeof(IContentType).Name: + RefreshContentOfContentTypes(ci.Value.refreshedIds.Concat(ci.Value.otherIds).Distinct().ToArray()); + break; + case var itemType when itemType == typeof(IMediaType).Name: + RefreshMediaOfMediaTypes(ci.Value.refreshedIds.Concat(ci.Value.otherIds).Distinct().ToArray()); + break; + case var itemType when itemType == typeof(IMemberType).Name: + RefreshMemberOfMemberTypes(ci.Value.refreshedIds.Concat(ci.Value.otherIds).Distinct().ToArray()); + break; + } + } + + //Delete all content of this content/media/member type that is in any content indexer by looking up matched examine docs + foreach (var id in ci.Value.removedIds) + { + foreach (var index in _examineManager.IndexProviders.Values.OfType()) + { + var searcher = index.GetSearcher(); + + var page = 0; + var total = long.MaxValue; + while (page * pageSize < total) + { + //paging with examine, see https://shazwazza.com/post/paging-with-examine/ + var results = searcher.Search( + searcher.CreateCriteria().Field("nodeType", id).Compile(), + maxResults: pageSize * (page + 1)); + total = results.TotalItemCount; + var paged = results.Skip(page * pageSize); + + foreach (var item in paged) + if (int.TryParse(item.Id, out var contentId)) + DeleteIndexForEntity(contentId, false); + } + } + } + } + } + + private void RefreshMemberOfMemberTypes(int[] memberTypeIds) + { + const int pageSize = 500; + + var memberTypes = _services.MemberTypeService.GetAll(memberTypeIds); + foreach(var memberType in memberTypes) + { var page = 0; var total = long.MaxValue; while (page * pageSize < total) { - 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)); + var memberToRefresh = _services.MemberService.GetAll( + page++, pageSize, out total, "LoginName", Direction.Ascending, + memberType.Alias); - //track which Ids have their paths are published - var publishChecked = new Dictionary(); - - foreach (var c in contentToRefresh) + foreach (var c in memberToRefresh) { - IContent published = null; - 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); + ReIndexForMember(c); } } } + } - //Delete all content of this content type that is in any content indexer by looking up matched examine docs - foreach(var id in removedIds) + private void RefreshMediaOfMediaTypes(int[] mediaTypeIds) + { + const int pageSize = 500; + var page = 0; + var total = long.MaxValue; + while (page * pageSize < total) { - foreach(var index in _examineManager.IndexProviders.Values.OfType()) + var mediaToRefresh = _services.MediaService.GetPagedOfTypes( + //Re-index all content of these types + mediaTypeIds, + page++, pageSize, out total, null, + Ordering.By("Path", Direction.Ascending)); + + foreach (var c in mediaToRefresh) { - var searcher = index.GetSearcher(); - - var page = 0; - var total = long.MaxValue; - while (page * pageSize < total) - { - //paging with examine, see https://shazwazza.com/post/paging-with-examine/ - var results = searcher.Search( - searcher.CreateCriteria().Field("nodeType", id).Compile(), - maxResults: pageSize * (page + 1)); - total = results.TotalItemCount; - var paged = results.Skip(page * pageSize); - - foreach(var item in paged) - if (int.TryParse(item.Id, out var contentId)) - DeleteIndexForEntity(contentId, false); - } + ReIndexForMedia(c, c.Trashed == false); + } + } + } + + private void RefreshContentOfContentTypes(int[] contentTypeIds) + { + const int pageSize = 500; + var page = 0; + var total = long.MaxValue; + while (page * pageSize < total) + { + var contentToRefresh = _services.ContentService.GetPagedOfTypes( + //Re-index all content of these types + contentTypeIds, + 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) + { + IContent published = null; + 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 = _services.ContentService.IsPathPublished(c); + publishChecked[c.Id] = isPublished; + if (isPublished) + published = c; + } + } + + ReIndexForContent(c, published); } - - } }