From f7c70bde1e11a5744bb5c8c9ca36aef50c0f0282 Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 7 Jan 2019 23:49:29 +1100 Subject: [PATCH] Fixing up the PublishedContentQuery.Search implementation 3920 --- src/Umbraco.Examine/ContentValueSetBuilder.cs | 6 +- .../ContentValueSetValidator.cs | 2 +- src/Umbraco.Examine/MediaValueSetBuilder.cs | 2 +- src/Umbraco.Examine/MemberValueSetBuilder.cs | 2 +- src/Umbraco.Examine/Umbraco.Examine.csproj | 2 +- .../UmbracoFieldDefinitionCollection.cs | 6 +- src/Umbraco.Examine/UmbracoMemberIndex.cs | 31 +------ .../PublishedContentMoreTests.cs | 2 +- src/Umbraco.Tests/Umbraco.Tests.csproj | 2 +- .../UmbracoContentValueSetValidatorTests.cs | 6 +- src/Umbraco.Web.UI/Umbraco.Web.UI.csproj | 2 +- src/Umbraco.Web/ExamineExtensions.cs | 3 +- src/Umbraco.Web/PublishedContentQuery.cs | 84 ++++++++++++++++++- .../Routing/ContentFinderByLegacy404.cs | 2 +- src/Umbraco.Web/Search/UmbracoTreeSearcher.cs | 6 +- src/Umbraco.Web/Umbraco.Web.csproj | 2 +- src/Umbraco.Web/UmbracoHelper.cs | 2 +- 17 files changed, 106 insertions(+), 56 deletions(-) diff --git a/src/Umbraco.Examine/ContentValueSetBuilder.cs b/src/Umbraco.Examine/ContentValueSetBuilder.cs index 07e5e4565b..18cd5e311f 100644 --- a/src/Umbraco.Examine/ContentValueSetBuilder.cs +++ b/src/Umbraco.Examine/ContentValueSetBuilder.cs @@ -44,7 +44,7 @@ namespace Umbraco.Examine {"icon", c.ContentType.Icon.Yield()}, {UmbracoExamineIndex.PublishedFieldName, new object[] {c.Published ? 1 : 0}}, //Always add invariant published value {"id", new object[] {c.Id}}, - {"key", new object[] {c.Key}}, + {UmbracoExamineIndex.NodeKeyFieldName, new object[] {c.Key}}, {"parentID", new object[] {c.Level > 1 ? c.ParentId : -1}}, {"level", new object[] {c.Level}}, {"creatorID", new object[] {c.CreatorId}}, @@ -61,12 +61,12 @@ namespace Umbraco.Examine {"writerName",(c.GetWriterProfile(_userService)?.Name ?? "??").Yield() }, {"writerID", new object[] {c.WriterId}}, {"template", new object[] {c.Template?.Id ?? 0}}, - {UmbracoContentIndex.VariesByCultureFieldName, new object[] {0}}, + {UmbracoContentIndex.VariesByCultureFieldName, new object[] {"n"}}, }; if (isVariant) { - values[UmbracoContentIndex.VariesByCultureFieldName] = new object[] { 1 }; + values[UmbracoContentIndex.VariesByCultureFieldName] = new object[] { "y" }; foreach (var culture in c.AvailableCultures) { diff --git a/src/Umbraco.Examine/ContentValueSetValidator.cs b/src/Umbraco.Examine/ContentValueSetValidator.cs index d4f6ceb15f..bcceafc272 100644 --- a/src/Umbraco.Examine/ContentValueSetValidator.cs +++ b/src/Umbraco.Examine/ContentValueSetValidator.cs @@ -100,7 +100,7 @@ namespace Umbraco.Examine //deal with variants, if there are unpublished variants than we need to remove them from the value set if (valueSet.Values.TryGetValue(UmbracoContentIndex.VariesByCultureFieldName, out var variesByCulture) - && variesByCulture.Count > 0 && variesByCulture[0].Equals(1)) + && variesByCulture.Count > 0 && variesByCulture[0].Equals("y")) { //so this valueset is for a content that varies by culture, now check for non-published cultures and remove those values foreach(var publishField in valueSet.Values.Where(x => x.Key.StartsWith($"{UmbracoExamineIndex.PublishedFieldName}_")).ToList()) diff --git a/src/Umbraco.Examine/MediaValueSetBuilder.cs b/src/Umbraco.Examine/MediaValueSetBuilder.cs index f0e5e895e6..2676093eeb 100644 --- a/src/Umbraco.Examine/MediaValueSetBuilder.cs +++ b/src/Umbraco.Examine/MediaValueSetBuilder.cs @@ -32,7 +32,7 @@ namespace Umbraco.Examine { {"icon", m.ContentType.Icon.Yield()}, {"id", new object[] {m.Id}}, - {"key", new object[] {m.Key}}, + {UmbracoExamineIndex.NodeKeyFieldName, new object[] {m.Key}}, {"parentID", new object[] {m.Level > 1 ? m.ParentId : -1}}, {"level", new object[] {m.Level}}, {"creatorID", new object[] {m.CreatorId}}, diff --git a/src/Umbraco.Examine/MemberValueSetBuilder.cs b/src/Umbraco.Examine/MemberValueSetBuilder.cs index 9864aba18d..d9f0b7806d 100644 --- a/src/Umbraco.Examine/MemberValueSetBuilder.cs +++ b/src/Umbraco.Examine/MemberValueSetBuilder.cs @@ -23,7 +23,7 @@ namespace Umbraco.Examine { {"icon", m.ContentType.Icon.Yield()}, {"id", new object[] {m.Id}}, - {"key", new object[] {m.Key}}, + {UmbracoExamineIndex.NodeKeyFieldName, new object[] {m.Key}}, {"parentID", new object[] {m.Level > 1 ? m.ParentId : -1}}, {"level", new object[] {m.Level}}, {"creatorID", new object[] {m.CreatorId}}, diff --git a/src/Umbraco.Examine/Umbraco.Examine.csproj b/src/Umbraco.Examine/Umbraco.Examine.csproj index a68131da0d..6864329bff 100644 --- a/src/Umbraco.Examine/Umbraco.Examine.csproj +++ b/src/Umbraco.Examine/Umbraco.Examine.csproj @@ -48,7 +48,7 @@ - + diff --git a/src/Umbraco.Examine/UmbracoFieldDefinitionCollection.cs b/src/Umbraco.Examine/UmbracoFieldDefinitionCollection.cs index 97d1f68727..43c9553400 100644 --- a/src/Umbraco.Examine/UmbracoFieldDefinitionCollection.cs +++ b/src/Umbraco.Examine/UmbracoFieldDefinitionCollection.cs @@ -30,7 +30,7 @@ namespace Umbraco.Examine new FieldDefinition("createDate", FieldDefinitionTypes.DateTime), new FieldDefinition("updateDate", FieldDefinitionTypes.DateTime), - new FieldDefinition("key", FieldDefinitionTypes.InvariantCultureIgnoreCase), + new FieldDefinition(UmbracoExamineIndex.NodeKeyFieldName, FieldDefinitionTypes.InvariantCultureIgnoreCase), new FieldDefinition("version", FieldDefinitionTypes.Raw), new FieldDefinition("nodeType", FieldDefinitionTypes.InvariantCultureIgnoreCase), new FieldDefinition("template", FieldDefinitionTypes.Raw), @@ -40,9 +40,9 @@ namespace Umbraco.Examine new FieldDefinition("email", FieldDefinitionTypes.EmailAddress), new FieldDefinition(UmbracoExamineIndex.PublishedFieldName, FieldDefinitionTypes.Raw), - new FieldDefinition(UmbracoExamineIndex.NodeKeyFieldName, FieldDefinitionTypes.Raw), new FieldDefinition(UmbracoExamineIndex.IndexPathFieldName, FieldDefinitionTypes.Raw), - new FieldDefinition(UmbracoExamineIndex.IconFieldName, FieldDefinitionTypes.Raw) + new FieldDefinition(UmbracoExamineIndex.IconFieldName, FieldDefinitionTypes.Raw), + new FieldDefinition(UmbracoContentIndex.VariesByCultureFieldName, FieldDefinitionTypes.Raw), }; ///// diff --git a/src/Umbraco.Examine/UmbracoMemberIndex.cs b/src/Umbraco.Examine/UmbracoMemberIndex.cs index 9782f94fe4..fbf8a1cc0f 100644 --- a/src/Umbraco.Examine/UmbracoMemberIndex.cs +++ b/src/Umbraco.Examine/UmbracoMemberIndex.cs @@ -32,35 +32,6 @@ namespace Umbraco.Examine base(name, luceneDirectory, fieldDefinitions, analyzer, profilingLogger, validator) { } - - /// - /// Overridden to ensure that the umbraco system field definitions are in place - /// - /// - /// - protected override FieldValueTypeCollection CreateFieldValueTypes(IReadOnlyDictionary indexValueTypesFactory = null) - { - var keyDef = new FieldDefinition("__key", FieldDefinitionTypes.Raw); - FieldDefinitionCollection.TryAdd(keyDef); - - return base.CreateFieldValueTypes(indexValueTypesFactory); - } - - /// - /// Ensure some custom values are added to the index - /// - /// - protected override void OnTransformingIndexValues(IndexingItemEventArgs e) - { - base.OnTransformingIndexValues(e); - - if (e.ValueSet.Values.TryGetValue("key", out var key) && e.ValueSet.Values.ContainsKey("__key") == false) - { - //double __ prefix means it will be indexed as culture invariant - e.ValueSet.Values["__key"] = key; - } - - } - + } } diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentMoreTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentMoreTests.cs index c5b8e21870..5f3a51f4f6 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentMoreTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentMoreTests.cs @@ -195,7 +195,7 @@ namespace Umbraco.Tests.PublishedContent [Test] public void PublishedContentQueryTypedContentList() { - var query = new PublishedContentQuery(UmbracoContext.Current.ContentCache, UmbracoContext.Current.MediaCache); + var query = new PublishedContentQuery(UmbracoContext.Current.ContentCache, UmbracoContext.Current.MediaCache, UmbracoContext.Current.VariationContextAccessor); var result = query.Content(new[] { 1, 2, 4 }).ToArray(); Assert.AreEqual(2, result.Length); Assert.AreEqual(1, result[0].Id); diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index bed1281bf8..8eb6d086a1 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -77,7 +77,7 @@ - + 1.8.9 diff --git a/src/Umbraco.Tests/UmbracoExamine/UmbracoContentValueSetValidatorTests.cs b/src/Umbraco.Tests/UmbracoExamine/UmbracoContentValueSetValidatorTests.cs index 8d7a446ccb..08db6b35e0 100644 --- a/src/Umbraco.Tests/UmbracoExamine/UmbracoContentValueSetValidatorTests.cs +++ b/src/Umbraco.Tests/UmbracoExamine/UmbracoContentValueSetValidatorTests.cs @@ -237,7 +237,7 @@ namespace Umbraco.Tests.UmbracoExamine { ["hello"] = "world", ["path"] = "-1,555", - [UmbracoContentIndex.VariesByCultureFieldName] = 1, + [UmbracoContentIndex.VariesByCultureFieldName] = "y", [UmbracoExamineIndex.PublishedFieldName] = 0 })); Assert.AreEqual(ValueSetValidationResult.Failed, result); @@ -247,7 +247,7 @@ namespace Umbraco.Tests.UmbracoExamine { ["hello"] = "world", ["path"] = "-1,555", - [UmbracoContentIndex.VariesByCultureFieldName] = 1, + [UmbracoContentIndex.VariesByCultureFieldName] = "y", [UmbracoExamineIndex.PublishedFieldName] = 1 })); Assert.AreEqual(ValueSetValidationResult.Valid, result); @@ -257,7 +257,7 @@ namespace Umbraco.Tests.UmbracoExamine { ["hello"] = "world", ["path"] = "-1,555", - [UmbracoContentIndex.VariesByCultureFieldName] = 1, + [UmbracoContentIndex.VariesByCultureFieldName] = "y", [$"{UmbracoExamineIndex.PublishedFieldName}_en-us"] = 1, ["hello_en-us"] = "world", ["title_en-us"] = "my title", diff --git a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj index 2fc199d06a..dae0781926 100644 --- a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj +++ b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj @@ -88,7 +88,7 @@ - + diff --git a/src/Umbraco.Web/ExamineExtensions.cs b/src/Umbraco.Web/ExamineExtensions.cs index f1ed6c0659..9a9fa98d95 100644 --- a/src/Umbraco.Web/ExamineExtensions.cs +++ b/src/Umbraco.Web/ExamineExtensions.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Linq; using Examine; using Umbraco.Core; diff --git a/src/Umbraco.Web/PublishedContentQuery.cs b/src/Umbraco.Web/PublishedContentQuery.cs index 3d8f36ec1a..03a0e8dfb2 100644 --- a/src/Umbraco.Web/PublishedContentQuery.cs +++ b/src/Umbraco.Web/PublishedContentQuery.cs @@ -1,4 +1,5 @@ using System; +using System.Collections; using System.Collections.Generic; using System.Linq; using System.Reflection; @@ -24,16 +25,19 @@ namespace Umbraco.Web { private readonly IPublishedContentCache _contentCache; private readonly IPublishedMediaCache _mediaCache; + private readonly IVariationContextAccessor _variationContextAccessor; /// /// Constructor used to return results from the caches /// /// /// - public PublishedContentQuery(IPublishedContentCache contentCache, IPublishedMediaCache mediaCache) + /// + public PublishedContentQuery(IPublishedContentCache contentCache, IPublishedMediaCache mediaCache, IVariationContextAccessor variationContextAccessor) { _contentCache = contentCache ?? throw new ArgumentNullException(nameof(contentCache)); _mediaCache = mediaCache ?? throw new ArgumentNullException(nameof(mediaCache)); + _variationContextAccessor = variationContextAccessor ?? throw new ArgumentNullException(nameof(variationContextAccessor)); } #region Content @@ -201,6 +205,9 @@ namespace Umbraco.Web // default to max 500 results var count = skip == 0 && take == 0 ? 500 : skip + take; + //set this to the specific culture or to the culture in the request + culture = culture ?? _variationContextAccessor.VariationContext.Culture; + ISearchResults results; if (culture.IsNullOrWhiteSpace()) { @@ -211,7 +218,7 @@ namespace Umbraco.Web //get all index fields suffixed with the culture name supplied var cultureFields = new List(); var fields = umbIndex.GetFields(); - var qry = searcher.CreateQuery().Field(UmbracoContentIndex.VariesByCultureFieldName, 1); //must vary by culture + var qry = searcher.CreateQuery().Field(UmbracoContentIndex.VariesByCultureFieldName, "y"); //must vary by culture // ReSharper disable once LoopCanBeConvertedToQuery foreach (var field in fields) { @@ -225,7 +232,8 @@ namespace Umbraco.Web } totalRecords = results.TotalItemCount; - return results.ToPublishedSearchResults(_contentCache); + + return new CultureContextualSearchResults(results.ToPublishedSearchResults(_contentCache), _variationContextAccessor, culture); } /// @@ -245,6 +253,76 @@ namespace Umbraco.Web return results.ToPublishedSearchResults(_contentCache); } + /// + /// This is used to contextualize the values in the search results when enumerating over them so that the correct culture values are used + /// + private class CultureContextualSearchResults : IEnumerable + { + private readonly IEnumerable _wrapped; + private readonly IVariationContextAccessor _variationContextAccessor; + private readonly string _culture; + + public CultureContextualSearchResults(IEnumerable wrapped, IVariationContextAccessor variationContextAccessor, string culture) + { + _wrapped = wrapped; + _variationContextAccessor = variationContextAccessor; + _culture = culture; + } + + public IEnumerator GetEnumerator() + { + //We need to change the current culture to what is requested and then change it back + var originalContext = _variationContextAccessor.VariationContext; + if (!_culture.IsNullOrWhiteSpace() && !_culture.InvariantEquals(originalContext.Culture)) + _variationContextAccessor.VariationContext = new VariationContext(_culture); + + //now the IPublishedContent returned will be contextualized to the culture specified and will be reset when the enumerator is disposed + return new CultureContextualSearchResultsEnumerator(_wrapped.GetEnumerator(), _variationContextAccessor, originalContext); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + /// + /// Resets the variation context when this is disposed + /// + private class CultureContextualSearchResultsEnumerator : IEnumerator + { + private readonly IEnumerator _wrapped; + private readonly IVariationContextAccessor _variationContextAccessor; + private readonly VariationContext _originalContext; + + public CultureContextualSearchResultsEnumerator(IEnumerator wrapped, IVariationContextAccessor variationContextAccessor, VariationContext originalContext) + { + _wrapped = wrapped; + _variationContextAccessor = variationContextAccessor; + _originalContext = originalContext; + } + + public void Dispose() + { + _wrapped.Dispose(); + //reset + _variationContextAccessor.VariationContext = _originalContext; + } + + public bool MoveNext() + { + return _wrapped.MoveNext(); + } + + public void Reset() + { + _wrapped.Reset(); + } + + public PublishedSearchResult Current => _wrapped.Current; + object IEnumerator.Current => Current; + } + } + /// /// Matches a culture iso name suffix /// diff --git a/src/Umbraco.Web/Routing/ContentFinderByLegacy404.cs b/src/Umbraco.Web/Routing/ContentFinderByLegacy404.cs index 99b4e22b5a..1f01270bc6 100644 --- a/src/Umbraco.Web/Routing/ContentFinderByLegacy404.cs +++ b/src/Umbraco.Web/Routing/ContentFinderByLegacy404.cs @@ -63,7 +63,7 @@ namespace Umbraco.Web.Routing var error404 = NotFoundHandlerHelper.GetCurrentNotFoundPageId( _contentConfigSection.Error404Collection.ToArray(), _entityService, - new PublishedContentQuery(frequest.UmbracoContext.ContentCache, frequest.UmbracoContext.MediaCache), + new PublishedContentQuery(frequest.UmbracoContext.ContentCache, frequest.UmbracoContext.MediaCache, frequest.UmbracoContext.VariationContextAccessor), errorCulture); IPublishedContent content = null; diff --git a/src/Umbraco.Web/Search/UmbracoTreeSearcher.cs b/src/Umbraco.Web/Search/UmbracoTreeSearcher.cs index 47e73d383c..a60e5f1d1b 100644 --- a/src/Umbraco.Web/Search/UmbracoTreeSearcher.cs +++ b/src/Umbraco.Web/Search/UmbracoTreeSearcher.cs @@ -367,9 +367,9 @@ namespace Umbraco.Web.Search { m.AdditionalData["Email"] = result.Values["email"]; } - if (result.Values.ContainsKey("__key") && result.Values["__key"] != null) + if (result.Values.ContainsKey(UmbracoExamineIndex.NodeKeyFieldName) && result.Values[UmbracoExamineIndex.NodeKeyFieldName] != null) { - if (Guid.TryParse(result.Values["__key"], out var key)) + if (Guid.TryParse(result.Values[UmbracoExamineIndex.NodeKeyFieldName], out var key)) { m.Key = key; } @@ -416,7 +416,7 @@ namespace Umbraco.Web.Search if (intId.Success) { //if it varies by culture, return the default language URL - if (result.Values.TryGetValue(UmbracoContentIndex.VariesByCultureFieldName, out var varies) && varies == "1") + if (result.Values.TryGetValue(UmbracoContentIndex.VariesByCultureFieldName, out var varies) && varies == "y") { entity.AdditionalData["Url"] = _umbracoHelper.Url(intId.Result, defaultLang); } diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 7a695ff4ef..83268bb78e 100755 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -63,7 +63,7 @@ - + 2.6.2.25 diff --git a/src/Umbraco.Web/UmbracoHelper.cs b/src/Umbraco.Web/UmbracoHelper.cs index 6914efb3e2..f4be2a1700 100644 --- a/src/Umbraco.Web/UmbracoHelper.cs +++ b/src/Umbraco.Web/UmbracoHelper.cs @@ -106,7 +106,7 @@ namespace Umbraco.Web /// Gets the query context. /// public IPublishedContentQuery ContentQuery => _query ?? - (_query = new PublishedContentQuery(UmbracoContext.ContentCache, UmbracoContext.MediaCache)); + (_query = new PublishedContentQuery(UmbracoContext.ContentCache, UmbracoContext.MediaCache, UmbracoContext.VariationContextAccessor)); /// /// Gets the Umbraco context.