Back to Basics: Fetch Solr Index Field Name for Sitecore Fields Using Solr FieldNameTranslator
I have been meaning to write this post for a while as I have been using this nifty little feature in all my content search API implementations using the Solr provider.
Unsafe: Defining SearchResultItem classes using hard coded Solr fields names for IndexField data annotation attributes as follows:
Problems:
Solution:
Here is the cool part that actually fetches the Solr index field name value using FieldNameTranslator from the Solr provider:
Register the event with a patch config.
<configuration>
<sitecore>
<events>
<!-- Custom -->
<event name="item:saved">
<handler method="OnItemSaved" type="SomeNameSpace.ItemSavedEvent, SomeBinary">
</handler>
</event>
<!-- END Custom -->
</events>
</sitecore>
</configuration>
Step 4: Update your T4 template to generate the code with field names as constants similar to:
Although Step 4 seems rather straightforward, I am unable to fetch the Solr Index Field Name value using TDS provided T4 templates as follows as it returns an empty value:
If you know a better way to fetch the value in T4 templates, please do share.
hope you find this post useful and as always please leave comments and suggestions for improvements.
Unsafe: Defining SearchResultItem classes using hard coded Solr fields names for IndexField data annotation attributes as follows:
[IndexField("page_title")] public string PageTitle { get; set; }
Problems:
- Cause runtime errors when field names change in Sitecore
- Hard coded strings, sort of magic values
- Unmanageable code
Safe: Define SearchResultItem classes using strongly typed constants for data annotation attributes as follows:
[IndexField(SampleItem.PageTitleSolrIndexFieldName)] public string PageTitle { get; set; }
Solution:
- Compile time errors are way better than runtime errors
- More manageable code
- More readable code
- More dynamic query building
Trick: Fetch Solr translated field name values on template fields using FieldNameTranslator and reference them with code generation.
Step 1: Create a base template to store the Solr Index Field Name under /sitecore/templates/System/Templates/Sections.
Step 2: Inherit the base template onto Template Field template.at /sitecore/templates/System/Templates/Template field
Step 3: Write an ItemSaved event handler that will generate the value for Solr Index Field Name.
using Sitecore.Data; using Sitecore.Data.Items; using Sitecore.Data.Managers; using Sitecore.Diagnostics; using Sitecore.Events; using Sitecore.SecurityModel; using System; using System.Collections.Generic; namespace SomeNameSpace { public class ItemSavedEvent { public string Database { get; set; } public void OnItemSaved(object sender, EventArgs args) { var t = new Dictionary(); //get the arguments as Sitecore args, and make sure it isn't null var eventArgs = args as SitecoreEventArgs; Assert.IsNotNull(eventArgs, "eventArgs"); //grab the current item from the event args, and make sure it isn't null var item = eventArgs.Parameters[0] as Item; Assert.IsNotNull(item, "item"); //do not process if the item is not derived from Solr base template inherited on Template Field item if (!IsDerived(item, new ID("{2201D7D1-C260-45D1-90AA-5FC1F822417A}"))) { return; } try { item = UpdateSolrIndexFieldName(item); Log.Info(string.Format("ITEM_SAVE_UPDATE: {0}", item.Fields["Solr Index Field Name"]), this); } catch (Exception ex) { Log.Error(string.Format("ITEM_SAVE_EXCEPTION: {0}", ex.Message), this); } } private Item UpdateSolrIndexFieldName(Item item) { if (item != null) { try { using (new SecurityDisabler()) { item.Editing.BeginEdit(); try { item["Solr Index Field Name"] = SearchHelper.GetSolrFieldName(item.Name, item.ID); } catch (Exception ex) { Log.Error(string.Format("ITEM_SAVE_EXCEPTION: {0}", ex.Message), this); throw; } finally { item.Editing.EndEdit(); } } } catch (Exception ex) { Log.Error(string.Format("ITEM_SAVE_EXCEPTION: {0}", ex.Message), this); throw; } } return item; } private bool IsDerived(Item item, ID templateId, bool checkSecurity = false) { if (item == null) return false; if (templateId.IsNull) return false; var state = SecurityState.Enabled; if (!checkSecurity) state = SecurityState.Disabled; using (new SecurityStateSwitcher(state)) { var templateItem = item.Database.Templates[templateId]; var isDerived = false; if (templateItem != null) { var template = TemplateManager.GetTemplate(item); if (template == null) return false; isDerived = template.ID == templateItem.ID || template.DescendsFrom(templateItem.ID); } return isDerived; } } } }
Here is the cool part that actually fetches the Solr index field name value using FieldNameTranslator from the Solr provider:
using Glass.Mapper.Sc; using Sitecore.ContentSearch; using Sitecore.ContentSearch.Linq; using Sitecore.ContentSearch.Linq.Utilities; using Sitecore.ContentSearch.SolrProvider; using Sitecore.Data; using System; using System.Collections.Generic; using System.Linq; using Sitecore.Data.Items; namespace SomeNameSpace { public static class SearchHelper { private static ISearchIndex _masterIndex = SolrContentSearchManager.GetIndex("sitecore_master_index"); public static string GetSolrFieldName(string sitecoreFieldName, ID sitecoreFieldId) { //for system fields replace double _ with single _ and spaces with empty string if (sitecoreFieldName.StartsWith("__")) return sitecoreFieldName.Replace("__", "_").Replace(" ", string.Empty).ToLower(); var masterDb = Sitecore.Data.Database.GetDatabase("master"); //get Sitecore field type var sitecoreFieldType = masterDb.GetItem(sitecoreFieldId).Fields["Type"].Value; //get Sitecore field type name var fieldTypeConfiguration = _masterIndex.Configuration.FieldMap.GetFieldConfigurationByFieldTypeName(sitecoreFieldType); try { //convert to system field type var type = Type.GetType(fieldTypeConfiguration.Attributes["type"]); //get the Solr field name based on the Sitecore field name and Sitecore field type. //if fieldNameTranslator is null, you have not configured Solr correctly //http://www.sitecorevarun.com/2015/09/resolved-sitecore-solr-provider-error.html return _masterIndex.FieldNameTranslator.GetIndexFieldName(sitecoreFieldName, type); } catch (Exception ex) { //for all custom sitecore types that are not defined in the field map in default index configuration throw ex; } } } }
Register the event with a patch config.
<configuration>
<sitecore>
<events>
<!-- Custom -->
<event name="item:saved">
<handler method="OnItemSaved" type="SomeNameSpace.ItemSavedEvent, SomeBinary">
</handler>
</event>
<!-- END Custom -->
</events>
</sitecore>
</configuration>
Step 4: Update your T4 template to generate the code with field names as constants similar to:
/// SampleItem /// [SitecoreType(TemplateId="76036f5e-cbce-46d1-af0a-4143f9b557aa")] //, Cachable = true public partial class SampleItem : GlassBase, ISampleItem { public const string TemplateIdString = "76036f5e-cbce-46d1-af0a-4143f9b557aa"; public static readonly ID TemplateId = new ID(TemplateIdString); public const string TemplateName = "Sample Item"; public static readonly ID TitleFieldId = new ID("75577384-3c97-45da-a847-81b00500e250"); public const string TitleFieldName = "Page Title"; public const string TitleSolrIndexFieldName = "Page_Title_t"; ////// Path: /sitecore/templates/Sample/Sample Item ///ID: 76036f5e-cbce-46d1-af0a-4143f9b557aa ////// The Page Title field. /// [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Team Development for Sitecore - GlassItem.tt", "1.0")] [SitecoreField(SampleItem.TitleFieldName)] public virtual string PageTitle {get; set;} }Field Type: Single-Line Text ///Field ID: 75577384-3c97-45da-a847-81b00500e250 ///Custom Data: ///
Although Step 4 seems rather straightforward, I am unable to fetch the Solr Index Field Name value using TDS provided T4 templates as follows as it returns an empty value:
public const string <#= GetPropertyName(field) #>SolrIndexFieldName = "<#=GetValue(field.SitecoreFields, "Solr Index Field Name")#>";
If you know a better way to fetch the value in T4 templates, please do share.
hope you find this post useful and as always please leave comments and suggestions for improvements.
This comment has been removed by a blog administrator.
ReplyDeleteThis comment has been removed by a blog administrator.
ReplyDeleteThis comment has been removed by a blog administrator.
ReplyDeleteThis comment has been removed by a blog administrator.
ReplyDeleteThis comment has been removed by a blog administrator.
ReplyDelete