From 2de465e8f397f4cd87550984ea2648e4477e0d8c Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 14 Nov 2016 17:39:28 +0100 Subject: [PATCH] Ensures that indexes are rebuild for items that have had their content type's or property type's aliases changed, also ensures that the content refresher kicks in if a property type alias has changed (this wasn't previously being done) --- src/Umbraco.Core/Models/ContentTypeBase.cs | 2 +- .../Cache/ContentTypeCacheRefresher.cs | 73 +++++++++-------- src/Umbraco.Web/Search/ExamineEvents.cs | 80 ++++++++++++++++++- 3 files changed, 114 insertions(+), 41 deletions(-) diff --git a/src/Umbraco.Core/Models/ContentTypeBase.cs b/src/Umbraco.Core/Models/ContentTypeBase.cs index 2982713e5a..88476f946d 100644 --- a/src/Umbraco.Core/Models/ContentTypeBase.cs +++ b/src/Umbraco.Core/Models/ContentTypeBase.cs @@ -326,7 +326,7 @@ namespace Umbraco.Core.Models } } - /// + /// /// A boolean flag indicating if a property type has been removed from this instance. /// /// diff --git a/src/Umbraco.Web/Cache/ContentTypeCacheRefresher.cs b/src/Umbraco.Web/Cache/ContentTypeCacheRefresher.cs index 44a6efe9ff..246571d479 100644 --- a/src/Umbraco.Web/Cache/ContentTypeCacheRefresher.cs +++ b/src/Umbraco.Web/Cache/ContentTypeCacheRefresher.cs @@ -32,7 +32,7 @@ namespace Umbraco.Web.Cache /// /// /// - private static JsonPayload[] DeserializeFromJsonPayload(string json) + internal static JsonPayload[] DeserializeFromJsonPayload(string json) { var serializer = new JavaScriptSerializer(); var jsonObject = serializer.Deserialize(json); @@ -45,30 +45,28 @@ namespace Umbraco.Web.Cache /// /// if the item was deleted /// - private static JsonPayload FromContentType(IContentTypeBase contentType, bool isDeleted = false) + internal static JsonPayload FromContentType(IContentTypeBase contentType, bool isDeleted = false) { var payload = new JsonPayload - { - Alias = contentType.Alias, - Id = contentType.Id, - PropertyTypeIds = contentType.PropertyTypes.Select(x => x.Id).ToArray(), - //either IContentType or IMediaType or IMemberType - Type = (contentType is IContentType) - ? typeof(IContentType).Name - : (contentType is IMediaType) + { + Alias = contentType.Alias, + Id = contentType.Id, + PropertyTypeIds = contentType.PropertyTypes.Select(x => x.Id).ToArray(), + //either IContentType or IMediaType or IMemberType + Type = (contentType is IContentType) + ? typeof(IContentType).Name + : (contentType is IMediaType) ? typeof(IMediaType).Name : typeof(IMemberType).Name, - DescendantPayloads = contentType.Descendants().Select(x => FromContentType(x)).ToArray(), - WasDeleted = isDeleted - }; - //here we need to check if the alias of the content type changed or if one of the properties was removed. - var dirty = contentType as IRememberBeingDirty; - if (dirty != null) - { - payload.PropertyRemoved = dirty.WasPropertyDirty("HasPropertyTypeBeenRemoved"); - payload.AliasChanged = dirty.WasPropertyDirty("Alias"); - payload.IsNew = dirty.WasPropertyDirty("HasIdentity"); - } + DescendantPayloads = contentType.Descendants().Select(x => FromContentType(x)).ToArray(), + WasDeleted = isDeleted, + PropertyRemoved = contentType.WasPropertyDirty("HasPropertyTypeBeenRemoved"), + AliasChanged = contentType.WasPropertyDirty("Alias"), + PropertyTypeAliasChanged = contentType.PropertyTypes.Any(x => x.WasPropertyDirty("Alias")), + IsNew = contentType.WasPropertyDirty("HasIdentity") + }; + + return payload; } @@ -90,7 +88,7 @@ namespace Umbraco.Web.Cache #region Sub classes - private class JsonPayload + internal class JsonPayload { public JsonPayload() { @@ -103,6 +101,7 @@ namespace Umbraco.Web.Cache public string Type { get; set; } public bool AliasChanged { get; set; } public bool PropertyRemoved { get; set; } + public bool PropertyTypeAliasChanged { get; set; } public JsonPayload[] DescendantPayloads { get; set; } public bool WasDeleted { get; set; } public bool IsNew { get; set; } @@ -190,21 +189,21 @@ namespace Umbraco.Web.Cache ApplicationContext.Current.ApplicationCache.RuntimeCache.ClearCacheByKeySearch(CacheKeys.IdToKeyCacheKey); ApplicationContext.Current.ApplicationCache.RuntimeCache.ClearCacheByKeySearch(CacheKeys.KeyToIdCacheKey); - payloads.ForEach(payload => + foreach (var payload in payloads) + { + //clear the cache for each item + ClearContentTypeCache(payload); + + //we only need to do this for IContentType NOT for IMediaType, we don't want to refresh the whole cache. + //if the item was deleted or the alias changed or property removed then we need to refresh the content. + //and, don't refresh the cache if it is new. + if (payload.Type == typeof(IContentType).Name + && payload.IsNew == false + && (payload.WasDeleted || payload.AliasChanged || payload.PropertyRemoved || payload.PropertyTypeAliasChanged)) { - //clear the cache for each item - ClearContentTypeCache(payload); - - //we only need to do this for IContentType NOT for IMediaType, we don't want to refresh the whole cache. - //if the item was deleted or the alias changed or property removed then we need to refresh the content. - //and, don't refresh the cache if it is new. - if (payload.Type == typeof(IContentType).Name - && !payload.IsNew - && (payload.WasDeleted || payload.AliasChanged || payload.PropertyRemoved)) - { - needsContentRefresh = true; - } - }); + needsContentRefresh = true; + } + } //need to refresh the xml content cache if required if (needsContentRefresh) @@ -237,7 +236,7 @@ namespace Umbraco.Web.Cache //cache if only a media type has changed. //we don't want to update the routes cache if all of the content types here are new. if (payloads.Any(x => x.Type == typeof(IContentType).Name) - && !payloads.All(x => x.IsNew)) //if they are all new then don't proceed + && payloads.All(x => x.IsNew) == false) //if they are all new then don't proceed { // SD: we need to clear the routes cache here! // diff --git a/src/Umbraco.Web/Search/ExamineEvents.cs b/src/Umbraco.Web/Search/ExamineEvents.cs index 20701fe330..d02850bffe 100644 --- a/src/Umbraco.Web/Search/ExamineEvents.cs +++ b/src/Umbraco.Web/Search/ExamineEvents.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Xml; @@ -67,12 +68,12 @@ namespace Umbraco.Web.Search /// /// This is used to refresh content indexers IndexData based on the DataService whenever a content type is changed since - /// properties may have been added/removed + /// properties may have been added/removed, then we need to re-index any required data if aliases have been changed /// /// /// /// - /// See: http://issues.umbraco.org/issue/U4-4798 + /// See: http://issues.umbraco.org/issue/U4-4798, http://issues.umbraco.org/issue/U4-7833 /// static void ContentTypeCacheRefresherCacheUpdated(ContentTypeCacheRefresher sender, CacheRefresherEventArgs e) { @@ -81,6 +82,79 @@ namespace Umbraco.Web.Search { provider.RefreshIndexerDataFromDataService(); } + + if (e.MessageType == MessageType.RefreshByJson) + { + var contentTypesChanged = new HashSet(); + var mediaTypesChanged = new HashSet(); + var memberTypesChanged = new HashSet(); + + var payloads = ContentTypeCacheRefresher.DeserializeFromJsonPayload(e.MessageObject.ToString()); + foreach (var payload in payloads) + { + if (payload.IsNew == false + && (payload.WasDeleted || payload.AliasChanged || payload.PropertyRemoved || payload.PropertyTypeAliasChanged)) + { + //if we get here it means that some aliases have changed and the indexes for those particular doc types will need to be updated + if (payload.Type == typeof(IContentType).Name) + { + //if it is content + contentTypesChanged.Add(payload.Alias); + } + else if (payload.Type == typeof(IMediaType).Name) + { + //if it is media + mediaTypesChanged.Add(payload.Alias); + } + else if (payload.Type == typeof(IMemberType).Name) + { + //if it is members + memberTypesChanged.Add(payload.Alias); + } + } + } + + //TODO: We need to update Examine to support re-indexing multiple items at once instead of one by one which will speed up + // the re-indexing process, we don't want to revert to rebuilding the whole thing! + + if (contentTypesChanged.Count > 0) + { + foreach (var alias in contentTypesChanged) + { + var ctType = ApplicationContext.Current.Services.ContentTypeService.GetContentType(alias); + var contentItems = ApplicationContext.Current.Services.ContentService.GetContentOfContentType(ctType.Id); + foreach (var contentItem in contentItems) + { + ReIndexForContent(contentItem, contentItem.HasPublishedVersion && contentItem.Trashed == false); + } + } + } + if (mediaTypesChanged.Count > 0) + { + foreach (var alias in mediaTypesChanged) + { + var ctType = ApplicationContext.Current.Services.ContentTypeService.GetMediaType(alias); + var mediaItems = ApplicationContext.Current.Services.MediaService.GetMediaOfMediaType(ctType.Id); + foreach (var mediaItem in mediaItems) + { + ReIndexForMedia(mediaItem, mediaItem.Trashed == false); + } + } + } + if (memberTypesChanged.Count > 0) + { + foreach (var alias in memberTypesChanged) + { + var ctType = ApplicationContext.Current.Services.MemberTypeService.Get(alias); + var memberItems = ApplicationContext.Current.Services.MemberService.GetMembersByMemberType(ctType.Id); + foreach (var memberItem in memberItems) + { + ReIndexForMember(memberItem); + } + } + } + } + } static void MemberCacheRefresherCacheUpdated(MemberCacheRefresher sender, CacheRefresherEventArgs e) @@ -432,7 +506,7 @@ namespace Umbraco.Web.Search //add an icon attribute to get indexed xml.Add(new XAttribute("icon", sender.ContentType.Icon)); - ExamineManager.Instance.ReIndexNode( + ExamineManager.Instance.ReIndexNode( xml, IndexTypes.Content, ExamineManager.Instance.IndexProviderCollection.OfType()