Sitecore Content Search API - Lucene Part 1 - A simple example

Sitecore Content Search API has been dubbed Linq to Sitecore by some, and is a great way to perform Lucene (or Solr but we will cover Lucene in this post) searching on your website.

This blog post will get you up and running as quickly as possible.

Please, please, please read the content search documentation too as of course there is so much more to the API than I'm going to cover here - we will barely scratch the surface.

Also check out Autohaus which is a fantastic resource showcasing some of the features in Sitecore 7.

Anyways, lets get up and running in a few easy steps.

1) Make sure you have Sitecore 7 installed.

2) Define your custom index configuration

You will need to define your custom index. Create two files in App_Config/Include

Sitecore.ContentSearch.Lucene.Index.MyCustomIndex.config

<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
<sitecore>
    <contentSearch>
        <configuration type="Sitecore.ContentSearch.LuceneProvider.LuceneSearchConfiguration, Sitecore.ContentSearch.LuceneProvider">
        <indexes hint="list:AddIndex">
              <index id="my_custom_index" type="Sitecore.ContentSearch.LuceneProvider.LuceneIndex, Sitecore.ContentSearch.LuceneProvider">
            <param desc="name">$(id)</param>
            <param desc="folder">$(id)</param>
            <!-- This initializes index property store. Id has to be set to the index id -->
            <param desc="propertyStore" ref="contentSearch/databasePropertyStore" param1="$(id)" />
            <strategies hint="list:AddStrategy">
              <!-- NOTE: order of these is controls the execution order -->
                  <strategy ref="contentSearch/indexUpdateStrategies/onPublishEndAsync" />
            </strategies>
            <commitPolicy hint="raw:SetCommitPolicy">
                  <policy type="Sitecore.ContentSearch.TimeIntervalCommitPolicy, Sitecore.ContentSearch" />
            </commitPolicy>
            <commitPolicyExecutor hint="raw:SetCommitPolicyExecutor">
              <policyExecutor type="Sitecore.ContentSearch.CommitPolicyExecutor, Sitecore.ContentSearch" />
            </commitPolicyExecutor>
            <locations hint="list:AddCrawler">            
                <crawler type="Sitecore.Support.ContentSearch.LuceneProvider.Crawlers.DefaultCrawler, Sitecore.Support.389569">
                <Database>web</Database>
                <Root>/sitecore</Root>
                </crawler>
            </locations>
        </index>        
        </indexes>
      </configuration>
    </contentSearch>
</sitecore>
</configuration>

WWW.MyCustomIndex.config

<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
<sitecore>
    <mycustomindex>
        <configuration type="Sitecore.Support.ContentSearch.LuceneProvider.LuceneIndexConfiguration, Sitecore.Support.389569">
            <IndexAllFields>true</IndexAllFields>
            <Analyzer ref="contentSearch/configuration/DefaultIndexConfiguration/Analyzer" />
            <fieldMap type="Sitecore.ContentSearch.FieldMap, Sitecore.ContentSearch">
                <!-- Fields to include in index. Alternatively use raw:AddFieldByType in the default config -->
                <fieldNames hint="raw:AddFieldByFieldName">
                    <fieldType fieldName="keywords" storageType="YES" indexType="TOKENIZED" vectorType="NO" boost="1f" type="System.String" settingType="Sitecore.ContentSearch.LuceneProvider.LuceneSearchFieldConfiguration, Sitecore.ContentSearch.LuceneProvider" />
                    <fieldType fieldName="display date" storageType="YES" indexType="TOKENIZED" vectorType="NO" boost="1f" type="System.DateTime" settingType="Sitecore.ContentSearch.LuceneProvider.LuceneSearchFieldConfiguration, Sitecore.ContentSearch.LuceneProvider" />                      
                        <Analyzer type="Sitecore.ContentSearch.LuceneProvider.Analyzers.LowerCaseKeywordAnalyzer, Sitecore.ContentSearch.LuceneProvider" />
                    </fieldType>
                </fieldNames>
            </fieldMap>
            <virtualFieldProcessors hint="raw:AddVirtualFieldProcessor">
                <virtualFieldProcessor fieldName="daterange" type="Sitecore.ContentSearch.VirtualFields.DateRangeFieldProcessor, Sitecore.ContentSearch" />
                <virtualFieldProcessor fieldName="_lastestversion" type="Sitecore.ContentSearch.VirtualFields.LatestVersionFieldProcessor, Sitecore.ContentSearch" />
                <virtualFieldProcessor fieldName="_url" type="Sitecore.ContentSearch.VirtualFields.UniqueIdFieldProcessor, Sitecore.ContentSearch" />
            </virtualFieldProcessors>
            <!-- Items of template to include in index -->
            <include hint="list:IncludeTemplate">
                <!-- Generic Page -->
                <genericPage>{7ED8892F-93FC-4D71-B53E-9146533998E7}</genericPage>
                <!-- FAQ Page -->
                <faqPage>{BAF2A066-A46C-42CA-83AE-303FED59BE84}</faqPage>
            </include>
            <include hint="list:ExcludeTemplate">
                <folder>{A87A00B1-E6DB-45AB-8B54-636FEC3B5523}</folder>
            </include>
            <exclude hint="list:ExcludeField">
                <__display_name>{B5E02AD9-D56F-4C41-A065-A133DB87BDEB}</__display_name>
            </exclude>
            <fields hint="raw:AddCustomField">
                <field luceneName="__smallCreatedDate" storageType="yes" indexType="tokenized" format="yyyyMMdd">__created</field>
                <field luceneName="__smallUpdatedDate" storageType="yes" indexType="tokenized" format="yyyyMMdd">__updated</field>
                <field luceneName="__smallDisplayDate" storageType="yes" indexType="tokenized" format="dd/MM/yy">display date</field>
            </fields>
            <fields hint="raw:AddComputedIndexField" />
            <fields hint="raw:RemoveSpecialFields">
                <remove type="both">AllTemplates</remove>
                <remove type="both">Created</remove>
                <remove type="both">Editor</remove>
                <remove type="both">Hidden</remove>
                <remove type="both">Icon</remove>
                <remove type="both">Links</remove>
                <remove type="both">Updated</remove>
            </fields>
            <FieldReaders type="Sitecore.ContentSearch.FieldReaders.FieldReaderMap, Sitecore.ContentSearch">
                <mapFieldByTypeName hint="raw:AddFieldReaderByFieldTypeName">
                    <fieldReader fieldTypeName="checkbox"                                           fieldNameFormat="{0}" fieldReaderType="Sitecore.ContentSearch.LuceneProvider.FieldReaders.CheckboxFieldReader, Sitecore.ContentSearch.LuceneProvider" />
                    <fieldReader fieldTypeName="date|datetime"                                      fieldNameFormat="{0}" fieldReaderType="Sitecore.ContentSearch.LuceneProvider.FieldReaders.DateFieldReader, Sitecore.ContentSearch.LuceneProvider" />
                    <fieldReader fieldTypeName="image"                                              fieldNameFormat="{0}" fieldReaderType="Sitecore.ContentSearch.FieldReaders.ImageFieldReader, Sitecore.ContentSearch" />
                    <fieldReader fieldTypeName="single-line text|multi-line text|text|memo"         fieldNameFormat="{0}" fieldReaderType="Sitecore.ContentSearch.FieldReaders.DefaultFieldReader, Sitecore.ContentSearch" />
                    <fieldReader fieldTypeName="html|rich text"                                     fieldNameFormat="{0}" fieldReaderType="Sitecore.ContentSearch.FieldReaders.RichTextFieldReader, Sitecore.ContentSearch" />
                    <fieldReader fieldTypeName="multilist with search|treelist with search"         fieldNameFormat="{0}" fieldReaderType="Sitecore.ContentSearch.FieldReaders.DelimitedListFieldReader, Sitecore.ContentSearch" />
                    <fieldReader fieldTypeName="checklist|multilist|treelist|treelistex|tree list"  fieldNameFormat="{0}" fieldReaderType="Sitecore.ContentSearch.FieldReaders.MultiListFieldReader, Sitecore.ContentSearch" />
                    <fieldReader fieldTypeName="icon|droplist|grouped droplist"                     fieldNameFormat="{0}" fieldReaderType="Sitecore.ContentSearch.FieldReaders.DefaultFieldReader, Sitecore.ContentSearch" />
                    <fieldReader fieldTypeName="name lookup value list|name value list"             fieldNameFormat="{0}" fieldReaderType="Sitecore.ContentSearch.FieldReaders.NameValueListFieldReader, Sitecore.ContentSearch" />
                    <fieldReader fieldTypeName="droplink|droptree|grouped droplink|tree"            fieldNameFormat="{0}" fieldReaderType="Sitecore.ContentSearch.FieldReaders.LookupFieldReader, Sitecore.ContentSearch" />
                    <fieldReader fieldTypeName="tracking"                                           fieldNameFormat="{0}" fieldReaderType="Sitecore.ContentSearch.FieldReaders.NullFieldReader, Sitecore.ContentSearch" />
                </mapFieldByTypeName>
            </FieldReaders>
            <IndexFieldStorageValueFormatter type="Sitecore.ContentSearch.LuceneProvider.Converters.LuceneIndexFieldStorageValueFormatter, Sitecore.ContentSearch.LuceneProvider" />
            <IndexDocumentPropertyMapper type="Sitecore.ContentSearch.LuceneProvider.DefaultLuceneDocumentTypeMapper, Sitecore.ContentSearch.LuceneProvider" />
        </configuration>
    </mycustomindex>
    <contentSearch>
        <configuration>
            <indexes hint="list:AddIndex">
                <index id="my_custom_index">                       
                    <Configuration ref="mycustomindex/configuration" />
                </index>            
            </indexes>
        </configuration>
    </contentSearch>        
</sitecore>
</configuration>

If you visit Sitecore, open the Control Panel and click Indexing you should be able to build your index.

There's alot of config there, and a few basic things to note:

fieldNames hint="raw:AddFieldByFieldName"

Here we define which fields should be indexed.

include hint="list:IncludeTemplate

Here we tell Lucene which types of items should be included in the index.

fields hint="raw:AddComputedIndexField"

I will cover computed fields in a seperate blog post but this enables you to define an indexed field value by executing some custom logic which is performed during indexing.

I could go into more detail but we could go on forever so please check out the documentation.

Performing a basic search

The following code will perform a basic search, again we will go a bit more advanced in the next post.

private void Test(string query)
    {
        using (var context = ContentSearchManager.GetIndex("my_custom_index")
            .CreateSearchContext())
        {
            IQueryable<CustomSearchResult> results = context.GetQueryable<CustomSearchResult>()
                .Where(i => i.Keywords.Contains(query));

            foreach (var result in results)
            {
                Response.Write(result + "<br/>");
            }               
        }
    }

and CustomSearchResult:

  public class CustomSearchResult
  {
      [IndexField("keywords")]
      public string Keywords { get; set; }
  }

You can download Sitecore.Support.389569 here.

Dave Leigh

Web, and long time Sitecore developer based in Bristol, UK, working at Valtech - valtech.co.uk - @valtech.
I occasionally do other things too.