Kentico offers a smart, index-based search engine powered by Lucene.NET. Kentico makes it easy to generate search indexes and implement a keyword based search out of the box. However, if you need a more detailed search with multiple filters or facets, and want to use Kentico's smart search for this, it must be custom developed. In this blog post, I will share some vital tips on creating a custom faceted search index in Kentico.

Kentico provides some documentation on how to create a custom search index, however the information provided doesn't take many use cases into consideration. For example, lets pretend we want to create a faceted search for kites. Kites can come in different colors and shapes. I have multiple kites that can be a variety of colors and I would like to be able to allow users to search for kites that are multiple different colors and of a specific shape. 

How to deal with delimiters and whitespace

The first thing you will need to do is get the data. Typically, the data I work with is already in Kentico so I create a custom SQL query in Kentico and use Kentico's DataQuery to get the data programmatically.

Kentico stores multiple selections separated by a pipe for the color field and I dont want to have to write a custom analyzer to use the pipe as a delimiter so I replace the pipe with a space. Unfortunately, this creates an issue with faceted search because now "navy blue|red|blue" will become "navy blue red blue", making it difficult to differentiate between "navy blue" and "blue". So first I must replace spaces with an underscore, then replace the pipe with a whitespace in my SQL query, resulting in the value "navy_blue red blue" for the color field. I also like to replace any special characters, such as "&", with an underscore.

Since you are storing the values with an underscore, dont forget to add the underscore to the faceted search values before querying the index!

If you would like to see how the values are being stored, I highly recommend downloading Luke in order to  better visualize your index and test the searches. 

Indexing custom fields

Now that I have my data, I want to index the colors and shape fields separate from the rest of the content, and query based on those field names. In order to do this I use

SearchHelper.AddGeneralField(ISearchDocument, FieldName, value, store?, tokenize?) 

where ISearchDocument is the search document, FieldName is the name of the field you want to search (color), value is the value of the field (navy_blue red blue), store is a boolean that asks if you want to store the value, tokenize is a boolean that asks if you want the search to find results that match individual tokens (subsets) of the field's value. I am storing and tokenizing the color field.

Generating the query

Now that the index is built, you need to be able to generate a query based on the selected values. In order to query the multiple selections that a user may have made while searching for kites with specific colors, the query should look something like +fieldname:(value1 value2), in my case it would be +color:(navy_blue red) if the user wanted a kite that has the colors navy blue and red. Take a look a Lucene's documentation for more information on query syntax. 

Get custom indexed data programmatically

If you need to get custom field values from the search results programmatically, you can use 

SearchContext.GetSearchValue(SearchIndexRowID, "CustomFieldName")

Displaying data from custom fields

Finally, when displaying your results, dont forget to use the following transformation in order to get the data from the custom fields. method 

GetSearchValue("CustomFieldName")