Index Nested Taxonomies

In this article you will find:

Goal

This article will provide information on how to extend the Hawksearch service to index Sitefinity’s Classifications with parent-child relations to create Nested Link List facets.

Prerequisite

Installed Connector - https://bridgeline.atlassian.net/wiki/spaces/CON/pages/3468472522

Registered custom search service - https://bridgeline.atlassian.net/wiki/spaces/CON/pages/3468466897

Add the Hierarchy Field

Hawksearch supports one Hierarchy field. You can select the field in Sitefinity’s Advanced Settings → Hawksearch (your-site-domain/Sitefinity/Administration/Settings/Advanced). The default field is “category”, but you can provide a custom field name.

After selecting the field you should provide the field name in the index “Additional fields for indexing“.

Navigate to Search indexes backend page (your-site-domain/Sitefinity/Administration/Search) and select an index. In Advanced section add the field name.

In the Hawksearch dashboard, the created field must be selected as the Hierarchy field. Open Workbench → Data Configuration → Fields and select the created field in the previous step, in this case Category. Make sure it is selected as Hierrchy Field before triggering reindexing from Sitefinity.

Extend the Hawksearch Service

In terms of performance, we want to control taxonomies for which types of content we want to export. In the example below, we provide a CustomContentType, in this case Car, for which we want to export the hierarchical taxonomies. You can easily add more than one content type by adding the full name of the Sitefinity content type to the validation on line 27. This will include the taxonomies selected for that content type and add them to the Hawksearch index.

using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; using Hawksearch.Search; using Telerik.OpenAccess; using Telerik.Sitefinity.Data; using Telerik.Sitefinity.GenericContent.Model; using Telerik.Sitefinity.Model; using Telerik.Sitefinity.Publishing; using Telerik.Sitefinity.Services.Search.Data; using Telerik.Sitefinity.Taxonomies; using Telerik.Sitefinity.Taxonomies.Model; using Telerik.Sitefinity.Utilities.TypeConverters; namespace SitefinityWebApp.Search { public class CustomSearchService : HawksearchService { private const string CustomContentType = "Telerik.Sitefinity.DynamicTypes.Model.Cars.Car"; public override void UpdateIndex(string name, IEnumerable<IDocument> documents) { var documentList = documents.ToList(); var contentType = this.GetContentType(documentList); if (string.Equals(contentType, CustomContentType, StringComparison.InvariantCultureIgnoreCase)) { var documentIds = documents.Select(d => d.Fields.FirstOrDefault(f => f.Name == "Id")?.Value.ToString()); var mappedManager = ManagerBase.GetMappedManager(contentType); var type = TypeResolutionService.ResolveType(contentType, false); var taxonomyManager = TaxonomyManager.GetManager(); var items = mappedManager .GetItems(type, string.Empty, string.Empty, 0, 0) .Cast<IDataItem>() .Where(i => i.GetPropertyValue<ContentLifecycleStatus>("Status") == ContentLifecycleStatus.Live && i.GetPropertyValue<bool>("Visible") && documentIds.Contains(i.Id.ToString())) .ToList(); var taxonomies = taxonomyManager.GetTaxa<HierarchicalTaxon>().ToList(); foreach (var document in documentList.Cast<Telerik.Sitefinity.Services.Search.Model.Document>()) { var id = document.Fields.FirstOrDefault(f => f.Name == "Id"); var documentId = Guid.Empty; if (id != null) { documentId = Guid.Parse(id.Value.ToString()); } var properties = TypeDescriptor.GetProperties(items.FirstOrDefault(m => m.Id == documentId)); foreach (PropertyDescriptor property in properties) { if (property != null) { if (property.PropertyType == typeof(TrackedList<Guid>)) { var taxonomyIds = items.FirstOrDefault(m => m.Id == documentId).GetPropertyValue<TrackedList<Guid>>(property.Name); var taxonomyNames = new List<string>(); foreach (var taxonomyId in taxonomyIds) { var taxon = taxonomies.FirstOrDefault(t => t.Id == taxonomyId); if (taxon != null && taxon.Parent != null) { taxonomyNames = this.ProcessTaxonomies(taxon); } } if (document.Fields.FirstOrDefault(f => f.Name == property.Name) != null) { document.Fields.FirstOrDefault(f => f.Name == property.Name).Value = taxonomyNames.ToArray(); } } } } } } base.UpdateIndex(name, documents); } private string GetContentType(List<IDocument> documentList) { var contentType = string.Empty; var doc = documentList.FirstOrDefault(); if (doc != null && doc.Fields.FirstOrDefault(f => f.Name == "ContentType") != null) { var contentTypeField = doc.Fields.FirstOrDefault(f => f.Name == "ContentType").Value; if (contentTypeField != null) { contentType = contentTypeField.ToString(); } } return contentType; } private List<string> ProcessTaxonomies(HierarchicalTaxon taxon) { var taxonomies = new List<string>(); taxonomies.Add(taxon.Id.ToString()); while (taxon.ParentId != Guid.Empty) { taxonomies.Add(taxon.ParentId.ToString()); taxon = taxon.Parent; } taxonomies.Reverse(); return taxonomies; } } }