Programmatically Creating Synonym Maps in Azure Search with Sitecore | Perficient Digital

Programmatically Creating Synonym Maps in Azure Search with Sitecore

When performing searches in your Sitecore site, users may not know the exact term they should be using to get quality search results. For example, a user on a healthcare site may search the term “heart” expecting search results related to terms like “cardiovascular” or “chest.” Even further, some search results may not even contain the word “heart” but only contain “cardiovascular”. Without a synonym map, searching “heart” would not bring up those results. If you do not know what synonym maps are and what they look like, I suggest doing some light reading here before continuing further. Essentially, a synonym map is a list of synonym rules that will inform Azure Search which words can be interchangeable when performing a search.

The Complexity that Sitecore Brings to Synonym Maps

Programmatically creating a synonym map in Azure Search in C# is very simple. In a non-Sitecore context, you can have a search index stored in your Azure Search Service that never gets swapped with a rebuild index. You can create the synonym map you want to create and apply it to the fields you wish to make use of the synonym map. You only have to perform this operation one time (and whenever you want to add more synonym rules to the synonym map). You will never have to worry about that synonym map being removed from the search index.

When Sitecore gets involved, an extra layer of complexity is added. When you re-index, the synonym map is effectively “torn down” leaving you to think the synonym map was deleted. This is not the case. When you re-index, a rebuild index is used to perform the re-index operation. Once that operation is completed successfully, that rebuild index is swapped with the index that is currently in use. Thus, the synonym map that you created and applied on the originally-active index does not exist on the now-active rebuilt index. You will then need to recreate and apply the synonym map on the newly-active index. This is problematic. How should the synonym map be maintained?

How to Get Around the Rebuild Index Problem

Have no fear! Getting around this rebuild problem is not difficult. First, we will make synonym rules content-authorable in the Sitecore Content Editor. We will then write C# code that will query Sitecore for those Sitecore items to create and apply a synonym map to fields in a Sitecore index in Azure Search. Last, we have two options here: create a PowerShell command that will run this C# code on a manual trigger/schedule or create a Sitecore task command and schedule items that will run this C# code. This blog post will show you how to do the latter option.

Step 1 – Creating the Content Authored Synonym Rules

The first step is to create a template that will be used to construct the synonym rules. The synonym item only has to contain a single-line text field to house the words that could be interchangeable with each other. Each synonym item a content-author creates will represent a single synonym rule in a synonym map. See the sample template below:

Synonym map in Azure template

Furthermore, I would suggest updating the Short Description standard field on the Synonym single-line text field item to inform content authors how we are going to be expecting synonyms to be added to the field. We are going to want content-authors to enter a comma-delimited string, so I added this short description as seen below:

Synonym item example

Now that we have created the item content authors will be creating to make synonym rules, we need to create a folder to house where these synonyms should live. If you are in an SXA solution, I recommend storing the synonym rules under the Data folder in your site. If you are not in an SXA solution, simply create a folder in some section that houses global data for your site. We will be polling this folder to retrieve the Synonym children items to construct the synonym map in the C# task. Here is an example of a set of synonym rules:

Synonym examples

The above screenshot shows three separate synonym rule items: one for synonyms with the word “book,” one for synonyms with the word “heart,” and one for synonyms with the word “spare.” The synonym rule I want for “book” is: “book, novel, writing.” The synonym rule I want for “heart” is: “heart, cardiovascular, chest.” The synonym rule I want for “spare” is: “spare, backup, leftover.” The end goal is to create a synonym map that looks like this:

{
   "name":"my-synonym-map",
   "format":"solr",
   "synonyms": "
      book, novel, writing\n
      heart, cardiovascular, chest\n
      spare, backup, leftover\n"
}

Step 2 – Creating and Applying the Synonym Map in a C# Task Using the Azure Search API

namespace SampleProject.Feature.Search.Tasks
{
  public class SynonymMapping
  {         
    public void Execute(Item[] items, CommandItem command, ScheduleItem schedule)
    {
      var synonymMapName = "my-synonym-map";
      var indexesToUpdate = new List<string>()
      {
        "custom_master_index",
        "custom_web_index",
      };

      CreateOrUpdateSynonymMap(synonymMapName);
      UpdateIndexesToUseSynonymMap(indexesToUpdate, synonymMapName);            
    }

    private void CreateOrUpdateSynonymMap(string synonymMapName)
    {      
      var searchServiceClient = // code to retrieve the search service client

      var webDatabase = Factory.GetDatabase("web");

      var synonymChildren = webDatabase
        .GetItem("<Insert Path to Synonyms Folder item or its Guid>")
        .Children.ToList();

      var synonymValues = new List<string>();

      foreach (var synonymItem in synonymChildren)
      {
        synonymValues.Add(synonymItem.Fields["<Insert Guid of Synonym field>"].Value);
      }

      var synonymMap = new SynonymMap()
      {
        Name = synonymMapName,
        Format = "solr",
        Synonyms = string.Join("\n", synonymValues)
      }

      searchServiceClient.SynonymMaps.CreateOrUpdate(synonymMap);
    }

    private void UpdateIndexesToUseSynonymMap(IEnumerable<string> indexesToUpdate, string synonymMapName)
    {      
      var searchServiceClient = // code to retrieve the search service client

      foreach (var indexToUpdate in indexesToUpdate)
      {
        var index = // code to retrieve index

        index.Fields.FirstOrDefault(f => f.Name == "samplecomputedfield_s")
          .SynonymMaps = new[] { synonymMapName };
      }
    }
  }
}

The parameters for the Execute method will come into play when we create the Sitecore Command and Task items in Step 3. In the code above, the first step is to query Sitecore to retrieve the content-authored synonym items. The next step is to construct the synonym map and add it to the search service. Once it is available for indexes to consume, we then specify what fields in an index we want the synonym map to work with. The code above adds the synonym map to the “samplecomputedfield_s” field.

Important note: creating and updating the synonym map on the search service will not impact your user searches until you specify specifically what fields will be using said synonym map.

Step 3 – Creating a Scheduled Task in Sitecore

In the Content Editor under the /sitecore/system/Tasks/Commands/ folder, create a Command item. This command item will tell Sitecore what C# code to run. The Command item for the code above would look like this:

Sitecore command item

The Type field specifies the the C# class followed by the DLL the class lives in. The next order of business is to create a Schedule item in the Content Editor under the /sitecore/system/Tasks/Schedules/ folder. This schedule item will tell Sitecore when to run the newly-created command above. The Schedule item for my example would look like this:

Synonym schedule item

This schedule item will run the command every 15 minutes. The 127 number tells Sitecore to run this 15-minute interval daily (more on how that number is determined later). The first two parameters in the Schedule field specify the start and end date. Boom! That’s it! That’s all you have to do to set up synonym mapping programmatically and avoid the issue where an index rebuild operation will remove your synonym map.

Tips

  1. Synonym rules will not chain together. If you had two rules that were “heart, chest” and “chest, soccer,” searching “heart” would not bring up search results for “soccer.”
  2. In order for a synonym to match, the user must type in the words found in the synonym rule correctly letter for letter. Using the same example above in Tip #1, searching “hart” will not bring up search results for “chest” because “heart” was not explicitly typed in.
  3. How does the number 127 from Step 3 represent all 7 days of the week? Each day of the week is assigned a value of a power of two starting with Sunday at 2 to the power of 0 up through Saturday at 2 to the power of 6. The sum of those values is the value you input as the third parameter in the Schedule field in Step 3. 127 is the sum of all the days of the week.
  4. Each synonym rule can have up to 20 terms in it. Each synonym map can have up 5,000 rules in a free service and up to 10,000 rules in a paid service.
  5. An index field can only have a single synonym map. Multiple synonym maps on a single index field are not currently supported.

Conclusion

Setting up a synonym map with Azure Search in a Sitecore context can be tricky. This blog post should take away that complexity for you. Cheers!

Leave a Reply