From 76d206a4badd7b267d861a36043be62cb7ab25cf Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 23 Jun 2021 12:46:50 -0600 Subject: [PATCH] Fixes back office searching --- src/Umbraco.Web/IPublishedContentQuery.cs | 4 +- .../Models/Mapping/EntityMapDefinition.cs | 2 +- .../Search/IUmbracoTreeSearcherFields.cs | 11 ++- .../Search/IUmbracoTreeSearcherFields2.cs | 17 +++-- src/Umbraco.Web/Search/UmbracoTreeSearcher.cs | 30 ++++++-- .../Search/UmbracoTreeSearcherFields.cs | 75 ++++++++++--------- 6 files changed, 84 insertions(+), 55 deletions(-) diff --git a/src/Umbraco.Web/IPublishedContentQuery.cs b/src/Umbraco.Web/IPublishedContentQuery.cs index f513c1ac02..369fb6ca3d 100644 --- a/src/Umbraco.Web/IPublishedContentQuery.cs +++ b/src/Umbraco.Web/IPublishedContentQuery.cs @@ -8,7 +8,7 @@ using Umbraco.Core.Xml; namespace Umbraco.Web { - using Examine = global::Examine; + // TODO: Merge this into IPublishedContentQuery for v9! public interface IPublishedContentQuery2 : IPublishedContentQuery { /// @@ -32,7 +32,7 @@ namespace Umbraco.Web /// /// While enumerating results, the ambient culture is changed to be the searched culture. /// - IEnumerable Search(string term, int skip, int take, out long totalRecords, string culture = "*", string indexName = Umbraco.Core.Constants.UmbracoIndexes.ExternalIndexName, ISet loadedFields = null); + IEnumerable Search(string term, int skip, int take, out long totalRecords, string culture = "*", string indexName = Constants.UmbracoIndexes.ExternalIndexName, ISet loadedFields = null); } /// diff --git a/src/Umbraco.Web/Models/Mapping/EntityMapDefinition.cs b/src/Umbraco.Web/Models/Mapping/EntityMapDefinition.cs index 1c4ca6087c..621487ae04 100644 --- a/src/Umbraco.Web/Models/Mapping/EntityMapDefinition.cs +++ b/src/Umbraco.Web/Models/Mapping/EntityMapDefinition.cs @@ -180,7 +180,7 @@ namespace Umbraco.Web.Models.Mapping target.Name = source.Values.ContainsKey("nodeName") ? source.Values["nodeName"] : "[no name]"; - var culture = context.GetCulture(); + var culture = context.GetCulture()?.ToLowerInvariant(); if(culture.IsNullOrWhiteSpace() == false) { target.Name = source.Values.ContainsKey($"nodeName_{culture}") ? source.Values[$"nodeName_{culture}"] : target.Name; diff --git a/src/Umbraco.Web/Search/IUmbracoTreeSearcherFields.cs b/src/Umbraco.Web/Search/IUmbracoTreeSearcherFields.cs index c5a6c53d19..b3a4ec7055 100644 --- a/src/Umbraco.Web/Search/IUmbracoTreeSearcherFields.cs +++ b/src/Umbraco.Web/Search/IUmbracoTreeSearcherFields.cs @@ -8,19 +8,22 @@ namespace Umbraco.Web.Search public interface IUmbracoTreeSearcherFields { /// - /// Propagate list of searchable fields for all node types + /// The default index fields that are searched on in the back office search for umbraco content entities. /// IEnumerable GetBackOfficeFields(); + /// - /// Propagate list of searchable fields for Members + /// The additional index fields that are searched on in the back office for member entities. /// IEnumerable GetBackOfficeMembersFields(); + /// - /// Propagate list of searchable fields for Media + /// The additional index fields that are searched on in the back office for media entities. /// IEnumerable GetBackOfficeMediaFields(); + /// - /// Propagate list of searchable fields for Documents + /// The additional index fields that are searched on in the back office for document entities. /// IEnumerable GetBackOfficeDocumentFields(); } diff --git a/src/Umbraco.Web/Search/IUmbracoTreeSearcherFields2.cs b/src/Umbraco.Web/Search/IUmbracoTreeSearcherFields2.cs index 42f592a965..da0cd26644 100644 --- a/src/Umbraco.Web/Search/IUmbracoTreeSearcherFields2.cs +++ b/src/Umbraco.Web/Search/IUmbracoTreeSearcherFields2.cs @@ -1,28 +1,29 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +using System.Collections.Generic; namespace Umbraco.Web.Search { + // TODO: Merge this interface to IUmbracoTreeSearcherFields for v9. + // We should probably make these method make a little more sense when they are combined so have + // a single method for getting fields to search and fields to load for each category. public interface IUmbracoTreeSearcherFields2 : IUmbracoTreeSearcherFields { /// /// Set of fields for all node types to be loaded /// ISet GetBackOfficeFieldsToLoad(); + /// - /// Set list of fields for Members to be loaded + /// Additional set list of fields for Members to be loaded /// ISet GetBackOfficeMembersFieldsToLoad(); + /// - /// Set of fields for Media to be loaded + /// Additional set of fields for Media to be loaded /// ISet GetBackOfficeMediaFieldsToLoad(); /// - /// Set of fields for Documents to be loaded + /// Additional set of fields for Documents to be loaded /// ISet GetBackOfficeDocumentFieldsToLoad(); } diff --git a/src/Umbraco.Web/Search/UmbracoTreeSearcher.cs b/src/Umbraco.Web/Search/UmbracoTreeSearcher.cs index 839656ebef..ed2d8d3c86 100644 --- a/src/Umbraco.Web/Search/UmbracoTreeSearcher.cs +++ b/src/Umbraco.Web/Search/UmbracoTreeSearcher.cs @@ -101,7 +101,9 @@ namespace Umbraco.Web.Search string type; var indexName = Constants.UmbracoIndexes.InternalIndexName; var fields = _umbracoTreeSearcherFields.GetBackOfficeFields().ToList(); - ISet fieldsToLoad = null; + ISet fieldsToLoad = _umbracoTreeSearcherFields is IUmbracoTreeSearcherFields2 searcherFields2 + ? new HashSet(searcherFields2.GetBackOfficeFieldsToLoad()) + : null; // TODO: WE should try to allow passing in a lucene raw query, however we will still need to do some manual string // manipulation for things like start paths, member types, etc... @@ -124,7 +126,10 @@ namespace Umbraco.Web.Search fields.AddRange(_umbracoTreeSearcherFields.GetBackOfficeMembersFields()); if (_umbracoTreeSearcherFields is IUmbracoTreeSearcherFields2 umbracoTreeSearcherFieldMember) { - fieldsToLoad = umbracoTreeSearcherFieldMember.GetBackOfficeMembersFieldsToLoad(); + foreach(var field in umbracoTreeSearcherFieldMember.GetBackOfficeMembersFieldsToLoad()) + { + fieldsToLoad.Add(field); + } } if (searchFrom != null && searchFrom != Constants.Conventions.MemberTypes.AllMembersListId && searchFrom.Trim() != "-1") { @@ -138,9 +143,13 @@ namespace Umbraco.Web.Search fields.AddRange(_umbracoTreeSearcherFields.GetBackOfficeMediaFields()); if (_umbracoTreeSearcherFields is IUmbracoTreeSearcherFields2 umbracoTreeSearcherFieldsMedia) { - fieldsToLoad = umbracoTreeSearcherFieldsMedia.GetBackOfficeMediaFieldsToLoad(); + foreach (var field in umbracoTreeSearcherFieldsMedia.GetBackOfficeMediaFieldsToLoad()) + { + fieldsToLoad.Add(field); + } } - var allMediaStartNodes = _umbracoContext.Security.CurrentUser.CalculateMediaStartNodeIds(_entityService _appCaches); + + var allMediaStartNodes = _umbracoContext.Security.CurrentUser.CalculateMediaStartNodeIds(_entityService, _appCaches); AppendPath(sb, UmbracoObjectTypes.Media, allMediaStartNodes, searchFrom, ignoreUserStartNodes, _entityService); break; case UmbracoEntityTypes.Document: @@ -148,7 +157,10 @@ namespace Umbraco.Web.Search fields.AddRange(_umbracoTreeSearcherFields.GetBackOfficeDocumentFields()); if (_umbracoTreeSearcherFields is IUmbracoTreeSearcherFields2 umbracoTreeSearcherFieldsDocument) { - fieldsToLoad = umbracoTreeSearcherFieldsDocument.GetBackOfficeDocumentFieldsToLoad(); + foreach (var field in umbracoTreeSearcherFieldsDocument.GetBackOfficeDocumentFieldsToLoad()) + { + fieldsToLoad.Add(field); + } } var allContentStartNodes = _umbracoContext.Security.CurrentUser.CalculateContentStartNodeIds(_entityService, _appCaches); AppendPath(sb, UmbracoObjectTypes.Document, allContentStartNodes, searchFrom, ignoreUserStartNodes, _entityService); @@ -168,7 +180,13 @@ namespace Umbraco.Web.Search return Enumerable.Empty(); } - var result = internalSearcher.CreateQuery().NativeQuery(sb.ToString()).SelectFields(fieldsToLoad) + var examineQuery = internalSearcher.CreateQuery().NativeQuery(sb.ToString()); + if (fieldsToLoad != null) + { + examineQuery.SelectFields(fieldsToLoad); + } + + var result = examineQuery //only return the number of items specified to read up to the amount of records to fill from 0 -> the number of items on the page requested .Execute(Convert.ToInt32(pageSize * (pageIndex + 1))); diff --git a/src/Umbraco.Web/Search/UmbracoTreeSearcherFields.cs b/src/Umbraco.Web/Search/UmbracoTreeSearcherFields.cs index 5a2fd91d18..3025f869b4 100644 --- a/src/Umbraco.Web/Search/UmbracoTreeSearcherFields.cs +++ b/src/Umbraco.Web/Search/UmbracoTreeSearcherFields.cs @@ -1,55 +1,62 @@ +using Examine; +using Examine.LuceneEngine.Providers; using System.Collections.Generic; using System.Linq; +using Umbraco.Core.Services; using Umbraco.Examine; namespace Umbraco.Web.Search { public class UmbracoTreeSearcherFields : IUmbracoTreeSearcherFields2 { - private IReadOnlyList _backOfficeFields = new List {"id", "__NodeId", "__Key"}; - public IEnumerable GetBackOfficeFields() + private IReadOnlyList _backOfficeFields = new List {"id", LuceneIndex.ItemIdFieldName, UmbracoExamineIndex.NodeKeyFieldName}; + private readonly ISet _backOfficeFieldsToLoad = new HashSet { "id", LuceneIndex.ItemIdFieldName, UmbracoExamineIndex.NodeKeyFieldName, "nodeName", UmbracoExamineIndex.IconFieldName, LuceneIndex.CategoryFieldName, "parentID", LuceneIndex.ItemTypeFieldName }; + private IReadOnlyList _backOfficeMediaFields = new List { UmbracoExamineIndex.UmbracoFileFieldName }; + private readonly ISet _backOfficeMediaFieldsToLoad = new HashSet { UmbracoExamineIndex.UmbracoFileFieldName }; + private IReadOnlyList _backOfficeMembersFields = new List { "email", "loginName" }; + private readonly ISet _backOfficeMembersFieldsToLoad = new HashSet { "email", "loginName" }; + private readonly ISet _backOfficeDocumentFieldsToLoad = new HashSet { UmbracoContentIndex.VariesByCultureFieldName }; + private readonly ILocalizationService _localizationService; + + public UmbracoTreeSearcherFields(ILocalizationService localizationService) { - return _backOfficeFields; + _localizationService = localizationService; } + /// + public IEnumerable GetBackOfficeFields() => _backOfficeFields; - private IReadOnlyList _backOfficeMembersFields = new List {"email", "loginName"}; - public IEnumerable GetBackOfficeMembersFields() - { - return _backOfficeMembersFields; - } - private IReadOnlyList _backOfficeMediaFields = new List {UmbracoExamineIndex.UmbracoFileFieldName }; - public IEnumerable GetBackOfficeMediaFields() - { - return _backOfficeMediaFields; - } - public IEnumerable GetBackOfficeDocumentFields() - { - return Enumerable.Empty(); - } + /// + public IEnumerable GetBackOfficeMembersFields() => _backOfficeMembersFields; - private readonly ISet _backOfficeFieldsToLoad = new HashSet { "id", "__NodeId", "__Key" }; - public ISet GetBackOfficeFieldsToLoad() - { - return _backOfficeFieldsToLoad; - } + /// + public IEnumerable GetBackOfficeMediaFields() => _backOfficeMediaFields; - private readonly ISet _backOfficeMembersFieldsToLoad = new HashSet { "id", "__NodeId", "__Key", "email", "loginName" }; - public ISet GetBackOfficeMembersFieldsToLoad() - { - return _backOfficeMembersFieldsToLoad; - } + /// + public IEnumerable GetBackOfficeDocumentFields() => Enumerable.Empty(); - private readonly ISet _backOfficeMediaFieldsToLoad = new HashSet { "id", "__NodeId", "__Key", UmbracoExamineIndex.UmbracoFileFieldName }; - public ISet GetBackOfficeMediaFieldsToLoad() - { - return _backOfficeMediaFieldsToLoad; - } - private readonly ISet _backOfficeDocumentFieldsToLoad = new HashSet { "id", "__NodeId", "__Key" }; + /// + public ISet GetBackOfficeFieldsToLoad() => _backOfficeFieldsToLoad; + /// + public ISet GetBackOfficeMembersFieldsToLoad() => _backOfficeMembersFieldsToLoad; + + /// + public ISet GetBackOfficeMediaFieldsToLoad() => _backOfficeMediaFieldsToLoad; + + /// public ISet GetBackOfficeDocumentFieldsToLoad() { - return _backOfficeDocumentFieldsToLoad; + var fields = _backOfficeDocumentFieldsToLoad; + + // We need to load all nodeName_* fields but we won't know those up front so need to get + // all langs (this is cached) + foreach(var field in _localizationService.GetAllLanguages().Select(x => "nodeName_" + x.IsoCode.ToLowerInvariant())) + { + fields.Add(field); + } + + return fields; } } }