Search code examples
searchumbracoumbraco8

Extend Umbraco back office search to search custom proerties not just title


What I would like to do is be able to type an custom property within the back office search. e.g. put the ISBN into the search field and have the results shown currently it always returns "no items found" as the search will only show results for the title node.

How do I enable the content search as seen in the image to search the data in the custom fields?

The data is in the internal index, I have checked the index is working and can see the result with "Examine Management" if I search via the custom data.

enter image description here


Solution

  • The solution is what I used to extend the search https://dev.to/skttl/how-to-customize-searching-in-umbraco-list-views-1knk

    Add a new file in the App_Code (SearchExtender)

    using System.Linq;
    using Examine;
    using Umbraco.Core;
    using Umbraco.Core.Cache;
    using Umbraco.Core.Configuration;
    using Umbraco.Core.Logging;
    using Umbraco.Core.Models;
    using Umbraco.Core.Persistence;
    using Umbraco.Core.Persistence.DatabaseModelDefinitions;
    using Umbraco.Core.PropertyEditors;
    using Umbraco.Core.Services;
    using Umbraco.Web;
    using Umbraco.Web.Editors;
    using Umbraco.Web.Models.ContentEditing;
    
    namespace SearchExtender
    {
        public class CustomListViewSearchController : ContentController
        {
            public CustomListViewSearchController(PropertyEditorCollection propertyEditors, IGlobalSettings globalSettings, IUmbracoContextAccessor umbracoContextAccessor, ISqlContext sqlContext, ServiceContext services, AppCaches appCaches, IProfilingLogger logger, IRuntimeState runtimeState, UmbracoHelper umbracoHelper)
                : base(propertyEditors, globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper)
            {
            }
    
            public PagedResult<ContentItemBasic<ContentPropertyBasic>> GetChildrenCustom(int id, string includeProperties, int pageNumber = 0, int pageSize = 0, string orderBy = "SortOrder", Direction orderDirection = Direction.Ascending, bool orderBySystemField = true, string filter = "", string cultureName = "")
            {
                // get the parent node, and its doctype alias from the content cache
                var parentNode = Services.ContentService.GetById(id);
                var parentNodeDocTypeAlias = parentNode != null ? parentNode.ContentType.Alias : null;
    
                // if the parent node is not "books", redirect to the core GetChildren() method
                if (parentNode?.ContentType.Alias != "books")
                {
                    return GetChildren(id, includeProperties, pageNumber, pageSize, orderBy, orderDirection, orderBySystemField, filter);
                }
    
                // if we can't get the InternalIndex, redirect to the core GetChildren() method, but log an error
                if (!ExamineManager.Instance.TryGetIndex("InternalIndex", out IIndex index))
                {
                    Logger.Error<CustomListViewSearchController>("Couldn't get InternalIndex for searching products in list view");
                    return GetChildren(id, includeProperties, pageNumber, pageSize, orderBy, orderDirection, orderBySystemField, filter);
                }
    
                // find children using Examine
    
                // create search criteria
                var searcher = index.GetSearcher();
                var searchCriteria = searcher.CreateQuery();
                var searchQuery = searchCriteria.Field("parentID", id);
    
                if (!filter.IsNullOrWhiteSpace())
                {
                    searchQuery = searchQuery.And().GroupedOr(new [] { "nodeName", "isbn" }, filter);
                }
    
                // do the search, but limit the results to the current page 👉 https://shazwazza.com/post/paging-with-examine/
                // pageNumber is not zero indexed in this, so just multiply pageSize by pageNumber
                var searchResults = searchQuery.Execute(pageSize * pageNumber);
    
                // get the results on the current page
                // pageNumber is not zero indexed in this, so subtract 1 from the pageNumber
                var totalChildren = searchResults.TotalItemCount;
                var pagedResultIds = searchResults.Skip((pageNumber > 0 ? pageNumber - 1 : 0) * pageSize).Select(x => x.Id).Select(x => int.Parse(x)).ToList();
                var children = Services.ContentService.GetByIds(pagedResultIds).ToList();
    
                if (totalChildren == 0)
                {
                    return new PagedResult<ContentItemBasic<ContentPropertyBasic>>(0, 0, 0);
                }
    
                var pagedResult = new PagedResult<ContentItemBasic<ContentPropertyBasic>>(totalChildren, pageNumber, pageSize);
                pagedResult.Items = children.Select(content =>
                    Mapper.Map<IContent, ContentItemBasic<ContentPropertyBasic>>(content))
                    .ToList(); // evaluate now
    
                return pagedResult;
    
            }
        }
    }
    

    change requests for /umbraco/backoffice/UmbracoApi/Content/GetChildren (the default endpoint for child nodes), and change it to my newly created one, which is located at /umbraco/backoffice/api/CustomListViewSearch/GetChildrenCustom.

    This is done easily by adding a js file containing an interceptor like this. Add file to /App_Plugins/CustomListViewSearch/CustomListViewSearch.js

    angular.module('umbraco.services').config([
       '$httpProvider',
       function ($httpProvider) {
    
           $httpProvider.interceptors.push(function ($q) {
               return {
                   'request': function (request) {
    
                       // Redirect any requests for the listview to our custom list view UI
                       if (request.url.indexOf("backoffice/UmbracoApi/Content/GetChildren?id=") > -1)
                           request.url = request.url.replace("backoffice/UmbracoApi/Content/GetChildren", "backoffice/api/CustomListViewSearch/GetChildrenCustom");
    
                       return request || $q.when(request);
                   }
               };
           });
    
       }]);
    

    a package.manifest file in my App_Plugins folder.

    {
      "javascript": [
        "/App_Plugins/CustomListViewSearch/CustomListViewSearch.js"
      ]
    }
    

    If the node Alais isnot working make sure its set in the documnt type (far right on document type name)