Merge branch 'v8/feature/examine-select' of https://github.com/nzdev/Umbraco-CMS into nzdev-v8/feature/examine-select

This commit is contained in:
Shannon
2021-06-23 11:23:58 -06:00
9 changed files with 117 additions and 11 deletions

View File

@@ -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
{
/// <summary>
@@ -21,7 +21,7 @@ namespace Umbraco.Examine
{
public const string VariesByCultureFieldName = SpecialFieldPrefix + "VariesByCulture";
protected ILocalizationService LanguageService { get; }
private readonly ISet<string> _idOnlyFieldSet = new HashSet<string> { "id" };
#region Constructors
/// <summary>
@@ -131,9 +131,10 @@ namespace Umbraco.Examine
var searcher = GetSearcher();
var c = searcher.CreateQuery();
var filtered = c.NativeQuery(rawQuery);
var results = filtered.Execute();
ProfilingLogger.Debug<string,long>(GetType(), "DeleteFromIndex with query: {Query} (found {TotalItems} results)", rawQuery, results.TotalItemCount);
var selectedFields = filtered.SelectFields(_idOnlyFieldSet);
var results = selectedFields.Execute();
ProfilingLogger.Debug<string, long>(GetType(), "DeleteFromIndex with query: {Query} (found {TotalItems} results)", rawQuery, results.TotalItemCount);
//need to queue a delete item for each one found
QueueIndexOperation(results.Select(r => new IndexOperation(new ValueSet(r.Id), IndexOperationType.Delete)));

View File

@@ -9,6 +9,31 @@ using Umbraco.Core.Xml;
namespace Umbraco.Web
{
using Examine = global::Examine;
public interface IPublishedContentQuery2 : IPublishedContentQuery
{
/// <summary>
/// Searches content.
/// </summary>
/// <param name="term">The term to search.</param>
/// <param name="skip">The amount of results to skip.</param>
/// <param name="take">The amount of results to take/return.</param>
/// <param name="totalRecords">The total amount of records.</param>
/// <param name="culture">The culture (defaults to a culture insensitive search).</param>
/// <param name="indexName">The name of the index to search (defaults to <see cref="Constants.UmbracoIndexes.ExternalIndexName" />).</param>
/// <param name="loadedFields">The fields to load in the results of the search (defaults to all fields loaded).</param>
/// <returns>
/// The search results.
/// </returns>
/// <remarks>
/// <para>
/// When the <paramref name="culture" /> 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.
/// </para>
/// <para>While enumerating results, the ambient culture is changed to be the searched culture.</para>
/// </remarks>
IEnumerable<PublishedSearchResult> Search(string term, int skip, int take, out long totalRecords, string culture = "*", string indexName = Umbraco.Core.Constants.UmbracoIndexes.ExternalIndexName, ISet<string> loadedFields = null);
}
/// <summary>
/// Query methods used for accessing strongly typed content in templates

View File

@@ -16,7 +16,7 @@ namespace Umbraco.Web
/// <summary>
/// A class used to query for published content, media items
/// </summary>
public class PublishedContentQuery : IPublishedContentQuery
public class PublishedContentQuery : IPublishedContentQuery2
{
private readonly IPublishedSnapshot _publishedSnapshot;
private readonly IVariationContextAccessor _variationContextAccessor;
@@ -190,6 +190,10 @@ namespace Umbraco.Web
/// <inheritdoc />
public IEnumerable<PublishedSearchResult> 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);
/// <inheritdoc />
public IEnumerable<PublishedSearchResult> Search(string term, int skip, int take, out long totalRecords, string culture = "*", string indexName = Constants.UmbracoIndexes.ExternalIndexName, ISet<string> 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()

View File

@@ -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<IBackgroundTask> _indexItemTaskRunner;
private readonly ISet<string> _idOnlyFieldSet = new HashSet<string> { "id" };
// the default enlist priority is 100
@@ -435,7 +437,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);

View File

@@ -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<string> _idOnlyFieldSet = new HashSet<string> { "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<string>.Succeed(); //if we can search we'll assume it's healthy
}
catch (Exception e)

View File

@@ -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
{
/// <summary>
/// Set of fields for all node types to be loaded
/// </summary>
ISet<string> GetBackOfficeFieldsToLoad();
/// <summary>
/// Set list of fields for Members to be loaded
/// </summary>
ISet<string> GetBackOfficeMembersFieldsToLoad();
/// <summary>
/// Set of fields for Media to be loaded
/// </summary>
ISet<string> GetBackOfficeMediaFieldsToLoad();
/// <summary>
/// Set of fields for Documents to be loaded
/// </summary>
ISet<string> GetBackOfficeDocumentFieldsToLoad();
}
}

View File

@@ -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.Cache;
using Umbraco.Core.Composing;
@@ -100,6 +101,7 @@ namespace Umbraco.Web.Search
string type;
var indexName = Constants.UmbracoIndexes.InternalIndexName;
var fields = _umbracoTreeSearcherFields.GetBackOfficeFields().ToList();
ISet<string> 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...
@@ -120,6 +122,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:");
@@ -130,12 +136,20 @@ namespace Umbraco.Web.Search
case UmbracoEntityTypes.Media:
type = "media";
fields.AddRange(_umbracoTreeSearcherFields.GetBackOfficeMediaFields());
var allMediaStartNodes = _umbracoContext.Security.CurrentUser.CalculateMediaStartNodeIds(_entityService, _appCaches);
if (_umbracoTreeSearcherFields is IUmbracoTreeSearcherFields2 umbracoTreeSearcherFieldsMedia)
{
fieldsToLoad = umbracoTreeSearcherFieldsMedia.GetBackOfficeMediaFieldsToLoad();
}
var allMediaStartNodes = _umbracoContext.Security.CurrentUser.CalculateMediaStartNodeIds(_entityService _appCaches);
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, _appCaches);
AppendPath(sb, UmbracoObjectTypes.Document, allContentStartNodes, searchFrom, ignoreUserStartNodes, _entityService);
break;
@@ -154,7 +168,7 @@ namespace Umbraco.Web.Search
return Enumerable.Empty<SearchResultEntity>();
}
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)));

View File

@@ -4,7 +4,7 @@ using Umbraco.Examine;
namespace Umbraco.Web.Search
{
public class UmbracoTreeSearcherFields : IUmbracoTreeSearcherFields
public class UmbracoTreeSearcherFields : IUmbracoTreeSearcherFields2
{
private IReadOnlyList<string> _backOfficeFields = new List<string> {"id", "__NodeId", "__Key"};
public IEnumerable<string> GetBackOfficeFields()
@@ -27,5 +27,29 @@ namespace Umbraco.Web.Search
{
return Enumerable.Empty<string>();
}
private readonly ISet<string> _backOfficeFieldsToLoad = new HashSet<string> { "id", "__NodeId", "__Key" };
public ISet<string> GetBackOfficeFieldsToLoad()
{
return _backOfficeFieldsToLoad;
}
private readonly ISet<string> _backOfficeMembersFieldsToLoad = new HashSet<string> { "id", "__NodeId", "__Key", "email", "loginName" };
public ISet<string> GetBackOfficeMembersFieldsToLoad()
{
return _backOfficeMembersFieldsToLoad;
}
private readonly ISet<string> _backOfficeMediaFieldsToLoad = new HashSet<string> { "id", "__NodeId", "__Key", UmbracoExamineIndex.UmbracoFileFieldName };
public ISet<string> GetBackOfficeMediaFieldsToLoad()
{
return _backOfficeMediaFieldsToLoad;
}
private readonly ISet<string> _backOfficeDocumentFieldsToLoad = new HashSet<string> { "id", "__NodeId", "__Key" };
public ISet<string> GetBackOfficeDocumentFieldsToLoad()
{
return _backOfficeDocumentFieldsToLoad;
}
}
}

View File

@@ -312,6 +312,7 @@
<Compile Include="Search\ExamineFinalComposer.cs" />
<Compile Include="Search\ExamineUserComponent.cs" />
<Compile Include="Search\IUmbracoTreeSearcherFields.cs" />
<Compile Include="Search\IUmbracoTreeSearcherFields2.cs" />
<Compile Include="Search\UmbracoTreeSearcherFields.cs" />
<Compile Include="Security\BackOfficeExternalLoginProviderErrorMiddlware.cs" />
<Compile Include="Security\BackOfficeExternalLoginProviderErrors.cs" />