Using the Solr spell checker
Solr has a spell checker feature. You use this feature to give users inline query suggestions based on other, similar terms. The source of the suggestions can be terms in a field in Solr, an external text file, or fields in other indexes.
You can configure the source in four ways:
-
IndexBasedSpellChecker
: use the Solr index as the source for a parallel index used for spell checking. You must define a field as the basis for the index terms. -
DirectSolrSpellChecker
: use terms from the Solr index without building a parallel index. -
FileBasedSpellChecker
: use an external file as a spelling dictionary. -
WordBreakSolrSpellChecker
: combine adjacent query terms and/or breaking terms into multiple word
We recommend these best practices:
-
If you use
IndexBasedSpellCheck
, copy terms from some fields (such as title, body, and so on) to another field that you create for spell checking purposes. -
If you use
DirectSolrSpellCheck
, when choosing a field to query for this spell checker, use one that has relatively little analysis performed on it, in particular stemming.
The Solr documentation provides more details.
Solr configuration
You can configure Solr in two ways:
-
Use an existing select request handler if all requests should generate a suggestion
-
Use a spell request handler if a dedicated request handler is required for serving the spellchecking suggestion
You configure Solr by editing the SolrConfig.xml
file.
Use an existing request handler
This enables a spell check for the _name
field of the master index:
-
Open the
solrconfig.xml
file, for exampleC:\solr-6.3.0\server\solr\configsets\sitecore_core_index\conf\solrconfig.xml
. -
Find the
<arr name="components">
node and enable it. It is disabled by default. -
Add
<str>spellcheck</str>
to the<arr name="components">
node. -
Go to the
<searchComponent name="spellcheck" class="solr.SpellCheckComponent">
node. -
Under the
<lst name="spellchecker">
node, change<str name="field">_text_</str>
to<str name="field">_name</str>
to enable spellchecking for the_name
field.
To check that it works:
-
Open Solr Admin in a browser, select the core index, or go directly with this link: http://localhost:8983/solr/#/sitecore_core_index/query
-
Select the spellcheck checkbox to enable spell check, and enter a spell check query in the spellcheck.q text field (
homt
) -
Click Execute Query.
Solr Admin looks similar to this if spell check works on the Solr server:
If you use multiple Solr cores, you must repeat this procedure for each of these cores.
Using a spell request handler
This enables spell checker for the _name field of the master index:
-
Open the
solrconfig.xml
file, for exampleC:\solr-6.3.0\server\solr\configsets\sitecore_core_index\conf\solrconfig.xml
. -
Locate the
<searchComponent name="spellcheck" class="solr.SpellCheckComponent">
node. -
Under the
<lst name="spellchecker">
node, change<str name="field">_text</str>
to<str name="field">_name</str>
. -
Save the
SolrConfig.xml
file and restart Solr.
To check that it works:
-
Go to
http://localhost:8983/solr/sitecore_master_index/spell?q=_name:homy&spellcheck=true&spellcheck.count=10&rows=100000000&version=2.2&spellcheck.build=true&wt=json&indent=true
in the browser.
If it works, the response looks like this:
Optional configuration
You can configure many other aspects of the Solr spell checker feature. You configure these parameters in the request handler section in the solrconfig.xml
file by adding nodes the <lst name="defaults">
node.
For example, this snippet adds spellcheck.count
with a value of 20
as the default count:
<requestHandler name="/select" class="solr.SearchHandler">
<!-- default values for query parameters can be specified, these
will be overridden by parameters in the request
-->
<lst name="defaults">
<str name="echoParams">explicit</str>
<int name="rows">10</int>
<str name="spellcheck">true</str>
<str name="spellcheck.count">20</str>
https://cwiki.apache.org/confluence/display/solr/Spell+Checking has more detail.
Sample code
This sample code shows how you can create a list of suggestion when a search term does not result in any results.
For example, if a user searches for the term homy
and there are no results, then you can return a list of suggestions that are close to homy
, including home
.
Sitecore exposes the two APIs that you can use:
-
Use this if your code uses an implementation of ISolrQuery:
RequestResponseshellSolrNetProxy.Query public static SolrQueryResults<T> Query<T>(this IProviderSearchContext context, ISolrQuery q, QueryOptions queryOptions) public static SolrQueryResults<T> Query<T>(this IProviderSearchContext context, string q, QueryOptions queryOptions)
-
Use this if your code does not use ISolrQuery. This is an easier implementation:
RequestResponseshellSolrNetProxy.GetSpellCheck public static SolrQueryResults<Dictionary<string, object>> GetSpellCheck(this IProviderSearchContext context, ISolrQuery q, SpellCheckHandlerQueryOptions options)
The following examples show two different ways of using Solr spell checker from Sitecore.
Using SolrNetProxy.Query
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="HighlightDemo.aspx.cs" Inherits="Sitecore.ContentSearch.SolrProvider.Example.HighlightDemo" %>
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="HighlightDemo.aspx.cs" Inherits="Sitecore.ContentSearch.SolrProvider.Example.HighlightDemo" %>
<%@ Import Namespace="Sitecore.ContentSearch.SearchTypes" %>
<%@ Import Namespace="System" %>
<%@ Import Namespace="System.Collections.Generic" %>
<%@ Import Namespace="Sitecore.ContentSearch" %>
<%@ Import Namespace="Sitecore.ContentSearch.SolrNetExtension" %>
<%@ Import Namespace="Sitecore.ContentSearch.SolrProvider.SolrNetIntegration" %>
<%@ Import Namespace="SolrNet.Commands.Parameters" %>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
<script runat="server">
protected void OnClick(object sender, EventArgs e)
{
FirstSenario();
}
private void FirstSenario()
{
var index = ContentSearchManager.GetIndex("sitecore_master_index");
var searchText = this.txtInput.Text;
if (string.IsNullOrEmpty(searchText))
{
//TODO prompt error
this.ShowResult("Please enter a search text!");
return;
}
using (var context = index.CreateSearchContext())
{
var results = context.Query<SearchResultItem>(string.Format("_name:{0}", searchText), new QueryOptions()
{
SpellCheck = new SpellCheckingParameters()
});
if (results == null || results.SpellChecking == null || results.SpellChecking.Count < 1)
{
this.ShowResult("No rows returned");
this.rptResults.DataSource = new List<string>();
this.rptResults.DataBind();
return;
}
var suggestions = new List<string>();
foreach (var term in results.SpellChecking)
{
foreach (var suggestion in term.Suggestions)
{
suggestions.Add(suggestion);
}
}
this.BindSuggestion(suggestions);
}
}
private void ShowResult(string result)
{
this.divResult.InnerHtml = result;
}
private void BindSuggestion(List<string> suggestions)
{
this.rptResults.DataSource = suggestions;
this.rptResults.DataBind();
}
</script>
</head>
<body>
<form id="form1" runat="server">
<div>
<h3>Spell Check Example </h3>
<h4>Group search result by title</h4>
<br/>
<asp:Label runat="server" Text="Name" AssociatedControlID="txtInput"></asp:Label>
<asp:TextBox runat="server" ID="txtInput"></asp:TextBox>
<asp:Button runat="server" Text="Search" OnClick="OnClick" />
<br/>
<div><b>Search Results</b></div>
<br/>
<div id="divResult" runat="server"></div>
<asp:Repeater id="rptResults" runat="server">
<HeaderTemplate>
<table cols="1" width="100%" style="padding: 5px">
</HeaderTemplate>
<ItemTemplate>
<tr>
<td><%# Container.DataItem %></td>
</tr>
</ItemTemplate>
<FooterTemplate>
</table>
</FooterTemplate>
</asp:Repeater>
</div>
</form>
</body>
</html>
Using SolrNetProxy.GetSpellCheck
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="SpellCheckDemo.aspx.cs" Inherits="Sitecore.ContentSearch.SolrProvider.Example.SpellCheckDemo" %>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
<style>
.resultBox {
padding: 5px 5px 5px 5px;
margin-bottom: 1px;
}
#divGroupFieldLists {
border-style: dashed;
}
</style>
</head>
<body>
<form id="form1" runat="server">
<div>
<h3>Spell Check Example </h3>
<h4>Group search result by title</h4>
<br/>
<asp:Label runat="server" Text="Name" AssociatedControlID="txtInput"></asp:Label>
<asp:TextBox runat="server" ID="txtInput"></asp:TextBox>
<asp:Button runat="server" Text="Search" OnClick="OnClick" />
<br/>
<div><b>Search Results</b></div>
<br/>
<div id="divResult" runat="server"></div>
<asp:Repeater id="rptResults" runat="server">
<HeaderTemplate>
<table cols="1" width="100%" style="padding: 5px">
</HeaderTemplate>
<ItemTemplate>
<tr>
<td><%# Container.DataItem %></td>
</tr>
</ItemTemplate>
<FooterTemplate>
</table>
</FooterTemplate>
</asp:Repeater>
</div>
</form>
</body>
</html>
SpellCheckDemo.aspx.cs
namespace Sitecore.ContentSearch.SolrProvider.Example
{
using System;
using System.Collections.Generic;
using Sitecore.ContentSearch.SolrNetExtension;
using Sitecore.ContentSearch.SolrProvider.SolrNetIntegration;
using SolrNet.Commands.Parameters;
using SolrNet;
public partial class SpellCheckDemo : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
}
protected void OnClick(object sender, EventArgs e)
{
FirstSenario();
}
private void FirstSenario()
{
var index = ContentSearchManager.GetIndex("sitecore_master_index");
var searchText = this.txtInput.Text;
if (string.IsNullOrEmpty(searchText))
{
//TODO prompt error
this.ShowResult("Please enter a search text!");
return;
}
using (var context = index.CreateSearchContext())
{
var results = context.GetSpellCheck(new SolrQuery(string.Format("_name:{0}", searchText)), new SpellCheckHandlerQueryOptions()
{
SpellCheck = new SpellCheckingParameters()
{
Count = 10,
Build = true
}
});
if (results == null || results.SpellChecking == null || results.SpellChecking.Count < 1)
{
this.ShowResult("No rows returned");
this.rptResults.DataSource = new List<string>();
this.rptResults.DataBind();
return;
}
var suggestions = new List<string>();
foreach (var term in results.SpellChecking)
{
foreach (var suggestion in term.Suggestions)
{
suggestions.Add(suggestion);
}
}
this.BindSuggestion(suggestions);
}
}
#region Helper Method
private void ShowResult(string result)
{
this.divResult.InnerHtml = result;
}
private void BindSuggestion(List<string> suggestions)
{
this.rptResults.DataSource = suggestions;
this.rptResults.DataBind();
}
#endregion
}
}