diff --git a/build/NuSpecs/UmbracoCms.Web.nuspec b/build/NuSpecs/UmbracoCms.Web.nuspec index 82d15d2b95..59bd7d75aa 100644 --- a/build/NuSpecs/UmbracoCms.Web.nuspec +++ b/build/NuSpecs/UmbracoCms.Web.nuspec @@ -28,7 +28,7 @@ - + diff --git a/src/Umbraco.Examine/Umbraco.Examine.csproj b/src/Umbraco.Examine/Umbraco.Examine.csproj index 0e0ee62139..517edf354c 100644 --- a/src/Umbraco.Examine/Umbraco.Examine.csproj +++ b/src/Umbraco.Examine/Umbraco.Examine.csproj @@ -49,7 +49,7 @@ - + 1.0.0-beta2-19324-01 runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/Umbraco.Examine/UmbracoContentIndex.cs b/src/Umbraco.Examine/UmbracoContentIndex.cs index 88033b1407..468e794110 100644 --- a/src/Umbraco.Examine/UmbracoContentIndex.cs +++ b/src/Umbraco.Examine/UmbracoContentIndex.cs @@ -11,7 +11,7 @@ using Lucene.Net.Store; using Umbraco.Core.Composing; using Umbraco.Core.Logging; using Examine.LuceneEngine; - +using Examine.Search; namespace Umbraco.Examine { /// @@ -21,7 +21,7 @@ namespace Umbraco.Examine { public const string VariesByCultureFieldName = SpecialFieldPrefix + "VariesByCulture"; protected ILocalizationService LanguageService { get; } - + private readonly ISet _idOnlyFieldSet = new HashSet { "id" }; #region Constructors /// @@ -131,8 +131,9 @@ namespace Umbraco.Examine var searcher = GetSearcher(); var c = searcher.CreateQuery(); var filtered = c.NativeQuery(rawQuery); - var results = filtered.Execute(); + var selectedFields = filtered.SelectFields(_idOnlyFieldSet); + var results = selectedFields.Execute(); ProfilingLogger.Debug(GetType(), "DeleteFromIndex with query: {Query} (found {TotalItems} results)", rawQuery, results.TotalItemCount); //need to queue a delete item for each one found diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index 2ac28aa7d7..97604df0c6 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -79,7 +79,7 @@ - + 1.8.14 diff --git a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj index 01d029cae0..69bdeba643 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/IPublishedContentQuery.cs b/src/Umbraco.Web/IPublishedContentQuery.cs index 7066475dc9..f513c1ac02 100644 --- a/src/Umbraco.Web/IPublishedContentQuery.cs +++ b/src/Umbraco.Web/IPublishedContentQuery.cs @@ -9,6 +9,31 @@ using Umbraco.Core.Xml; namespace Umbraco.Web { using Examine = global::Examine; + public interface IPublishedContentQuery2 : IPublishedContentQuery + { + /// + /// Searches content. + /// + /// The term to search. + /// The amount of results to skip. + /// The amount of results to take/return. + /// The total amount of records. + /// The culture (defaults to a culture insensitive search). + /// The name of the index to search (defaults to ). + /// The fields to load in the results of the search (defaults to all fields loaded). + /// + /// The search results. + /// + /// + /// + /// When the is not specified or is *, all cultures are searched. + /// To search for only invariant documents and fields use null. + /// When searching on a specific culture, all culture specific fields are searched for the provided culture and all invariant fields for all documents. + /// + /// 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); + } /// /// Query methods used for accessing strongly typed content in templates diff --git a/src/Umbraco.Web/PublishedContentQuery.cs b/src/Umbraco.Web/PublishedContentQuery.cs index d697898f33..760a4b1c82 100644 --- a/src/Umbraco.Web/PublishedContentQuery.cs +++ b/src/Umbraco.Web/PublishedContentQuery.cs @@ -16,7 +16,7 @@ namespace Umbraco.Web /// /// A class used to query for published content, media items /// - public class PublishedContentQuery : IPublishedContentQuery + public class PublishedContentQuery : IPublishedContentQuery2 { private readonly IPublishedSnapshot _publishedSnapshot; private readonly IVariationContextAccessor _variationContextAccessor; @@ -190,6 +190,10 @@ namespace Umbraco.Web /// public IEnumerable Search(string term, int skip, int take, out long totalRecords, string culture = "*", string indexName = Constants.UmbracoIndexes.ExternalIndexName) + => Search(term, skip, take, out totalRecords, culture, indexName, null); + + /// + public IEnumerable Search(string term, int skip, int take, out long totalRecords, string culture = "*", string indexName = Constants.UmbracoIndexes.ExternalIndexName, ISet loadedFields = null) { if (skip < 0) { @@ -212,7 +216,7 @@ namespace Umbraco.Web } var query = umbIndex.GetSearcher().CreateQuery(IndexTypes.Content); - + IQueryExecutor queryExecutor; if (culture == "*") { @@ -231,6 +235,10 @@ namespace Umbraco.Web var fields = umbIndex.GetCultureAndInvariantFields(culture).ToArray(); // Get all index fields suffixed with the culture name supplied queryExecutor = query.ManagedQuery(term, fields); } + if (loadedFields != null && queryExecutor is IBooleanOperation booleanOperation) + { + queryExecutor = booleanOperation.SelectFields(loadedFields); + } var results = skip == 0 && take == 0 ? queryExecutor.Execute() diff --git a/src/Umbraco.Web/Search/ExamineComponent.cs b/src/Umbraco.Web/Search/ExamineComponent.cs index c9d7b7cf56..4fa2427fac 100644 --- a/src/Umbraco.Web/Search/ExamineComponent.cs +++ b/src/Umbraco.Web/Search/ExamineComponent.cs @@ -19,6 +19,7 @@ using Umbraco.Core.Composing; using System.ComponentModel; using System.Threading; using Umbraco.Web.Scheduling; +using Examine.Search; namespace Umbraco.Web.Search { @@ -39,6 +40,7 @@ namespace Umbraco.Web.Search private readonly IProfilingLogger _logger; private readonly IUmbracoIndexesCreator _indexCreator; private readonly BackgroundTaskRunner _indexItemTaskRunner; + private readonly ISet _idOnlyFieldSet = new HashSet { "id" }; // the default enlist priority is 100 @@ -425,7 +427,7 @@ namespace Umbraco.Web.Search while (page * pageSize < total) { //paging with examine, see https://shazwazza.com/post/paging-with-examine/ - var results = searcher.CreateQuery().Field("nodeType", id.ToInvariantString()).Execute(maxResults: pageSize * (page + 1)); + var results = searcher.CreateQuery().Field("nodeType", id.ToInvariantString()).SelectFields(_idOnlyFieldSet).Execute(maxResults: pageSize * (page + 1)); total = results.TotalItemCount; var paged = results.Skip(page * pageSize); diff --git a/src/Umbraco.Web/Search/GenericIndexDiagnostics.cs b/src/Umbraco.Web/Search/GenericIndexDiagnostics.cs index cb25e1242a..c8d3a72a96 100644 --- a/src/Umbraco.Web/Search/GenericIndexDiagnostics.cs +++ b/src/Umbraco.Web/Search/GenericIndexDiagnostics.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using Examine; +using Examine.Search; using Umbraco.Core; using Umbraco.Core.Composing; using Umbraco.Examine; @@ -17,6 +18,7 @@ namespace Umbraco.Web.Search private readonly IIndex _index; private static readonly string[] IgnoreProperties = { "Description" }; + private readonly ISet _idOnlyFieldSet = new HashSet { "id" }; public GenericIndexDiagnostics(IIndex index) { _index = index; @@ -34,7 +36,7 @@ namespace Umbraco.Web.Search try { var searcher = _index.GetSearcher(); - var result = searcher.Search("test"); + var result = searcher.CreateQuery().ManagedQuery("test").SelectFields(_idOnlyFieldSet).Execute(1); return Attempt.Succeed(); //if we can search we'll assume it's healthy } catch (Exception e) diff --git a/src/Umbraco.Web/Search/IUmbracoTreeSearcherFields2.cs b/src/Umbraco.Web/Search/IUmbracoTreeSearcherFields2.cs new file mode 100644 index 0000000000..42f592a965 --- /dev/null +++ b/src/Umbraco.Web/Search/IUmbracoTreeSearcherFields2.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Umbraco.Web.Search +{ + 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 + /// + ISet GetBackOfficeMembersFieldsToLoad(); + /// + /// Set of fields for Media to be loaded + /// + ISet GetBackOfficeMediaFieldsToLoad(); + + /// + /// 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 410b654e32..44ba0766e0 100644 --- a/src/Umbraco.Web/Search/UmbracoTreeSearcher.cs +++ b/src/Umbraco.Web/Search/UmbracoTreeSearcher.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Text; using System.Text.RegularExpressions; using Examine; +using Examine.Search; using Umbraco.Core; using Umbraco.Core.Mapping; using Umbraco.Core.Models; @@ -82,6 +83,7 @@ namespace Umbraco.Web.Search string type; var indexName = Constants.UmbracoIndexes.InternalIndexName; var fields = _umbracoTreeSearcherFields.GetBackOfficeFields().ToList(); + ISet fieldsToLoad = 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... @@ -102,6 +104,10 @@ namespace Umbraco.Web.Search indexName = Constants.UmbracoIndexes.MembersIndexName; type = "member"; fields.AddRange(_umbracoTreeSearcherFields.GetBackOfficeMembersFields()); + if (_umbracoTreeSearcherFields is IUmbracoTreeSearcherFields2 umbracoTreeSearcherFieldMember) + { + fieldsToLoad = umbracoTreeSearcherFieldMember.GetBackOfficeMembersFieldsToLoad(); + } if (searchFrom != null && searchFrom != Constants.Conventions.MemberTypes.AllMembersListId && searchFrom.Trim() != "-1") { sb.Append("+__NodeTypeAlias:"); @@ -112,12 +118,20 @@ namespace Umbraco.Web.Search case UmbracoEntityTypes.Media: type = "media"; fields.AddRange(_umbracoTreeSearcherFields.GetBackOfficeMediaFields()); + if (_umbracoTreeSearcherFields is IUmbracoTreeSearcherFields2 umbracoTreeSearcherFieldsMedia) + { + fieldsToLoad = umbracoTreeSearcherFieldsMedia.GetBackOfficeMediaFieldsToLoad(); + } var allMediaStartNodes = _umbracoContext.Security.CurrentUser.CalculateMediaStartNodeIds(_entityService); AppendPath(sb, UmbracoObjectTypes.Media, allMediaStartNodes, searchFrom, ignoreUserStartNodes, _entityService); break; case UmbracoEntityTypes.Document: type = "content"; fields.AddRange(_umbracoTreeSearcherFields.GetBackOfficeDocumentFields()); + if (_umbracoTreeSearcherFields is IUmbracoTreeSearcherFields2 umbracoTreeSearcherFieldsDocument) + { + fieldsToLoad = umbracoTreeSearcherFieldsDocument.GetBackOfficeDocumentFieldsToLoad(); + } var allContentStartNodes = _umbracoContext.Security.CurrentUser.CalculateContentStartNodeIds(_entityService); AppendPath(sb, UmbracoObjectTypes.Document, allContentStartNodes, searchFrom, ignoreUserStartNodes, _entityService); break; @@ -136,7 +150,7 @@ namespace Umbraco.Web.Search return Enumerable.Empty(); } - var result = internalSearcher.CreateQuery().NativeQuery(sb.ToString()) + var result = internalSearcher.CreateQuery().NativeQuery(sb.ToString()).SelectFields(fieldsToLoad) //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 f90d7bc6b6..5a2fd91d18 100644 --- a/src/Umbraco.Web/Search/UmbracoTreeSearcherFields.cs +++ b/src/Umbraco.Web/Search/UmbracoTreeSearcherFields.cs @@ -4,7 +4,7 @@ using Umbraco.Examine; namespace Umbraco.Web.Search { - public class UmbracoTreeSearcherFields : IUmbracoTreeSearcherFields + public class UmbracoTreeSearcherFields : IUmbracoTreeSearcherFields2 { private IReadOnlyList _backOfficeFields = new List {"id", "__NodeId", "__Key"}; public IEnumerable GetBackOfficeFields() @@ -27,5 +27,29 @@ namespace Umbraco.Web.Search { return Enumerable.Empty(); } + + private readonly ISet _backOfficeFieldsToLoad = new HashSet { "id", "__NodeId", "__Key" }; + public ISet GetBackOfficeFieldsToLoad() + { + return _backOfficeFieldsToLoad; + } + + private readonly ISet _backOfficeMembersFieldsToLoad = new HashSet { "id", "__NodeId", "__Key", "email", "loginName" }; + public ISet GetBackOfficeMembersFieldsToLoad() + { + return _backOfficeMembersFieldsToLoad; + } + + 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 GetBackOfficeDocumentFieldsToLoad() + { + return _backOfficeDocumentFieldsToLoad; + } } } diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 8890d9cf25..189d2e02d1 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -63,7 +63,7 @@ - + 4.0.217 @@ -284,6 +284,7 @@ +