From 75b1d4729f9cdc067e97dd929332180b32de2f36 Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Wed, 3 Apr 2024 18:49:23 +0200 Subject: [PATCH] Examine entity search for documents, media and members (#15972) --- .../Item/SearchDocumentItemController.cs | 9 ++- .../Media/Item/SearchMediaItemController.cs | 9 ++- .../Member/Item/SearchMemberItemController.cs | 9 ++- .../Services/IExamineEntitySearchService.cs | 9 +++ .../UmbracoBuilder.Services.cs | 1 + .../Implement/ExamineEntitySearchService.cs | 59 +++++++++++++++++++ 6 files changed, 81 insertions(+), 15 deletions(-) create mode 100644 src/Umbraco.Core/Services/IExamineEntitySearchService.cs create mode 100644 src/Umbraco.Infrastructure/Services/Implement/ExamineEntitySearchService.cs diff --git a/src/Umbraco.Cms.Api.Management/Controllers/Document/Item/SearchDocumentItemController.cs b/src/Umbraco.Cms.Api.Management/Controllers/Document/Item/SearchDocumentItemController.cs index 54d500691e..bd39db19c4 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/Document/Item/SearchDocumentItemController.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/Document/Item/SearchDocumentItemController.cs @@ -12,12 +12,12 @@ namespace Umbraco.Cms.Api.Management.Controllers.Document.Item; [ApiVersion("1.0")] public class SearchDocumentItemController : DocumentItemControllerBase { - private readonly IEntitySearchService _entitySearchService; + private readonly IExamineEntitySearchService _examineEntitySearchService; private readonly IDocumentPresentationFactory _documentPresentationFactory; - public SearchDocumentItemController(IEntitySearchService entitySearchService, IDocumentPresentationFactory documentPresentationFactory) + public SearchDocumentItemController(IExamineEntitySearchService examineEntitySearchService, IDocumentPresentationFactory documentPresentationFactory) { - _entitySearchService = entitySearchService; + _examineEntitySearchService = examineEntitySearchService; _documentPresentationFactory = documentPresentationFactory; } @@ -26,8 +26,7 @@ public class SearchDocumentItemController : DocumentItemControllerBase [ProducesResponseType(typeof(PagedModel), StatusCodes.Status200OK)] public async Task Search(string query, int skip = 0, int take = 100) { - // FIXME: use Examine and handle user start nodes - PagedModel searchResult = _entitySearchService.Search(UmbracoObjectTypes.Document, query, skip, take); + PagedModel searchResult = _examineEntitySearchService.Search(UmbracoObjectTypes.Document, query, skip, take); var result = new PagedModel { Items = searchResult.Items.OfType().Select(_documentPresentationFactory.CreateItemResponseModel), diff --git a/src/Umbraco.Cms.Api.Management/Controllers/Media/Item/SearchMediaItemController.cs b/src/Umbraco.Cms.Api.Management/Controllers/Media/Item/SearchMediaItemController.cs index 7d506f1b87..67bb4f5c01 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/Media/Item/SearchMediaItemController.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/Media/Item/SearchMediaItemController.cs @@ -12,12 +12,12 @@ namespace Umbraco.Cms.Api.Management.Controllers.Media.Item; [ApiVersion("1.0")] public class SearchMediaItemController : MediaItemControllerBase { - private readonly IEntitySearchService _entitySearchService; + private readonly IExamineEntitySearchService _examineEntitySearchService; private readonly IMediaPresentationFactory _mediaPresentationFactory; - public SearchMediaItemController(IEntitySearchService entitySearchService, IMediaPresentationFactory mediaPresentationFactory) + public SearchMediaItemController(IExamineEntitySearchService examineEntitySearchService, IMediaPresentationFactory mediaPresentationFactory) { - _entitySearchService = entitySearchService; + _examineEntitySearchService = examineEntitySearchService; _mediaPresentationFactory = mediaPresentationFactory; } @@ -26,8 +26,7 @@ public class SearchMediaItemController : MediaItemControllerBase [ProducesResponseType(typeof(PagedModel), StatusCodes.Status200OK)] public async Task Search(string query, int skip = 0, int take = 100) { - // FIXME: use Examine and handle user start nodes - PagedModel searchResult = _entitySearchService.Search(UmbracoObjectTypes.Media, query, skip, take); + PagedModel searchResult = _examineEntitySearchService.Search(UmbracoObjectTypes.Media, query, skip, take); var result = new PagedModel { Items = searchResult.Items.OfType().Select(_mediaPresentationFactory.CreateItemResponseModel), diff --git a/src/Umbraco.Cms.Api.Management/Controllers/Member/Item/SearchMemberItemController.cs b/src/Umbraco.Cms.Api.Management/Controllers/Member/Item/SearchMemberItemController.cs index fe58481816..df6a9325f7 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/Member/Item/SearchMemberItemController.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/Member/Item/SearchMemberItemController.cs @@ -12,12 +12,12 @@ namespace Umbraco.Cms.Api.Management.Controllers.Member.Item; [ApiVersion("1.0")] public class SearchMemberItemController : MemberItemControllerBase { - private readonly IEntitySearchService _entitySearchService; + private readonly IExamineEntitySearchService _examineEntitySearchService; private readonly IMemberPresentationFactory _memberPresentationFactory; - public SearchMemberItemController(IEntitySearchService entitySearchService, IMemberPresentationFactory memberPresentationFactory) + public SearchMemberItemController(IExamineEntitySearchService examineEntitySearchService, IMemberPresentationFactory memberPresentationFactory) { - _entitySearchService = entitySearchService; + _examineEntitySearchService = examineEntitySearchService; _memberPresentationFactory = memberPresentationFactory; } @@ -26,8 +26,7 @@ public class SearchMemberItemController : MemberItemControllerBase [ProducesResponseType(typeof(PagedModel), StatusCodes.Status200OK)] public async Task Search(string query, int skip = 0, int take = 100) { - // FIXME: use Examine - PagedModel searchResult = _entitySearchService.Search(UmbracoObjectTypes.Member, query, skip, take); + PagedModel searchResult = _examineEntitySearchService.Search(UmbracoObjectTypes.Member, query, skip, take); var result = new PagedModel { Items = searchResult.Items.OfType().Select(_memberPresentationFactory.CreateItemResponseModel), diff --git a/src/Umbraco.Core/Services/IExamineEntitySearchService.cs b/src/Umbraco.Core/Services/IExamineEntitySearchService.cs new file mode 100644 index 0000000000..6d2ec5403d --- /dev/null +++ b/src/Umbraco.Core/Services/IExamineEntitySearchService.cs @@ -0,0 +1,9 @@ +using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Models.Entities; + +namespace Umbraco.Cms.Core.Services; + +public interface IExamineEntitySearchService +{ + PagedModel Search(UmbracoObjectTypes objectType, string query, int skip = 0, int take = 100, bool ignoreUserStartNodes = false); +} diff --git a/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.Services.cs b/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.Services.cs index a30fb5c3fd..33149240fb 100644 --- a/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.Services.cs +++ b/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.Services.cs @@ -63,6 +63,7 @@ public static partial class UmbracoBuilderExtensions builder.Services.AddUnique(); builder.Services.AddUnique(); builder.Services.AddUnique(); + builder.Services.AddUnique(); return builder; } diff --git a/src/Umbraco.Infrastructure/Services/Implement/ExamineEntitySearchService.cs b/src/Umbraco.Infrastructure/Services/Implement/ExamineEntitySearchService.cs new file mode 100644 index 0000000000..dbdd6132a8 --- /dev/null +++ b/src/Umbraco.Infrastructure/Services/Implement/ExamineEntitySearchService.cs @@ -0,0 +1,59 @@ +using Examine; +using Umbraco.Cms.Core; +using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Models.ContentEditing; +using Umbraco.Cms.Core.Models.Entities; +using Umbraco.Cms.Core.Services; +using Umbraco.Cms.Infrastructure.Examine; + +namespace Umbraco.Cms.Infrastructure.Services.Implement; + +internal sealed class ExamineEntitySearchService : IExamineEntitySearchService +{ + private readonly IBackOfficeExamineSearcher _backOfficeExamineSearcher; + private readonly IEntityService _entityService; + + public ExamineEntitySearchService(IBackOfficeExamineSearcher backOfficeExamineSearcher, IEntityService entityService) + { + _backOfficeExamineSearcher = backOfficeExamineSearcher; + _entityService = entityService; + } + + public PagedModel Search(UmbracoObjectTypes objectType, string query, int skip = 0, int take = 100, bool ignoreUserStartNodes = false) + { + UmbracoEntityTypes entityType = objectType switch + { + UmbracoObjectTypes.Document => UmbracoEntityTypes.Document, + UmbracoObjectTypes.Media => UmbracoEntityTypes.Media, + UmbracoObjectTypes.Member => UmbracoEntityTypes.Member, + _ => throw new NotSupportedException("This service only supports searching for documents, media and members") + }; + + PaginationHelper.ConvertSkipTakeToPaging(skip, take, out var pageNumber, out var pageSize); + + IEnumerable searchResults = _backOfficeExamineSearcher.Search( + query, + entityType, + pageSize, + pageNumber, + out var totalFound, + ignoreUserStartNodes: ignoreUserStartNodes); + + Guid[] keys = searchResults.Select( + result => + result.Values.TryGetValue(UmbracoExamineFieldNames.NodeKeyFieldName, out var keyValue) && + Guid.TryParse(keyValue, out Guid key) + ? key + : Guid.Empty) + .Where(key => key != Guid.Empty) + .ToArray(); + + return new PagedModel + { + Items = keys.Any() + ? _entityService.GetAll(objectType, keys) + : Enumerable.Empty(), + Total = totalFound + }; + } +}