265 lines
18 KiB
HTML
265 lines
18 KiB
HTML
<!DOCTYPE html>
|
||
<html>
|
||
<head>
|
||
<meta charset="utf-8" />
|
||
<meta http-equiv="X-UA-Compatible" content="IE=edge"><title>Spellchecking in retrieve and rank - Brainsteam</title><meta name="viewport" content="width=device-width, initial-scale=1">
|
||
<meta itemprop="name" content="Spellchecking in retrieve and rank">
|
||
<meta itemprop="description" content="Introduction Being able to deal with typos and incorrect spellings is an absolute must in any modern search facility. Humans can be lazy and clumsy and I personally often search for things with incorrect terms due to my sausage fingers. In this article I will explain how to turn on spelling suggestions in retrieve and rank so that if your users ask your system for something with a clumsy query, you can suggest spelling fixes for them so that they can submit another, more fruitful question to the system."><meta itemprop="datePublished" content="2015-11-17T21:41:09+00:00" />
|
||
<meta itemprop="dateModified" content="2015-11-17T21:41:09+00:00" />
|
||
<meta itemprop="wordCount" content="1073">
|
||
<meta itemprop="keywords" content="checker,improvements,rank,retrieve,search,solr,spell,spelling,suggestions,tuning,watson," /><meta property="og:title" content="Spellchecking in retrieve and rank" />
|
||
<meta property="og:description" content="Introduction Being able to deal with typos and incorrect spellings is an absolute must in any modern search facility. Humans can be lazy and clumsy and I personally often search for things with incorrect terms due to my sausage fingers. In this article I will explain how to turn on spelling suggestions in retrieve and rank so that if your users ask your system for something with a clumsy query, you can suggest spelling fixes for them so that they can submit another, more fruitful question to the system." />
|
||
<meta property="og:type" content="article" />
|
||
<meta property="og:url" content="https://brainsteam.co.uk/2015/11/17/spellchecking-in-retrieve-and-rank/" /><meta property="article:section" content="posts" />
|
||
<meta property="article:published_time" content="2015-11-17T21:41:09+00:00" />
|
||
<meta property="article:modified_time" content="2015-11-17T21:41:09+00:00" />
|
||
|
||
<meta name="twitter:card" content="summary"/>
|
||
<meta name="twitter:title" content="Spellchecking in retrieve and rank"/>
|
||
<meta name="twitter:description" content="Introduction Being able to deal with typos and incorrect spellings is an absolute must in any modern search facility. Humans can be lazy and clumsy and I personally often search for things with incorrect terms due to my sausage fingers. In this article I will explain how to turn on spelling suggestions in retrieve and rank so that if your users ask your system for something with a clumsy query, you can suggest spelling fixes for them so that they can submit another, more fruitful question to the system."/>
|
||
<link href='https://fonts.googleapis.com/css?family=Playfair+Display:700' rel='stylesheet' type='text/css'>
|
||
<link rel="stylesheet" type="text/css" media="screen" href="https://brainsteam.co.uk/css/normalize.css" />
|
||
<link rel="stylesheet" type="text/css" media="screen" href="https://brainsteam.co.uk/css/main.css" />
|
||
|
||
<link id="dark-scheme" rel="stylesheet" type="text/css" href="https://brainsteam.co.uk/css/dark.css" />
|
||
|
||
<script src="https://brainsteam.co.uk/js/feather.min.js"></script>
|
||
|
||
<script src="https://brainsteam.co.uk/js/main.js"></script>
|
||
</head>
|
||
|
||
<body>
|
||
<div class="container wrapper">
|
||
<div class="header">
|
||
|
||
<div class="avatar">
|
||
<a href="https://brainsteam.co.uk/">
|
||
<img src="/images/avatar.png" alt="Brainsteam" />
|
||
</a>
|
||
</div>
|
||
|
||
<h1 class="site-title"><a href="https://brainsteam.co.uk/">Brainsteam</a></h1>
|
||
<div class="site-description"><p>The irregular mental expulsions of a PhD student and CTO of Filament, my views are my own and do not represent my employers in any way.</p><nav class="nav social">
|
||
<ul class="flat"><li><a href="https://twitter.com/jamesravey/" title="Twitter" rel="me"><i data-feather="twitter"></i></a></li><li><a href="https://github.com/ravenscroftj" title="Github" rel="me"><i data-feather="github"></i></a></li><li><a href="/index.xml" title="RSS" rel="me"><i data-feather="rss"></i></a></li></ul>
|
||
</nav></div>
|
||
|
||
<nav class="nav">
|
||
<ul class="flat">
|
||
|
||
<li>
|
||
<a href="/">Home</a>
|
||
</li>
|
||
|
||
<li>
|
||
<a href="/tags">Tags</a>
|
||
</li>
|
||
|
||
<li>
|
||
<a href="https://jamesravey.me">About Me</a>
|
||
</li>
|
||
|
||
</ul>
|
||
</nav>
|
||
</div>
|
||
|
||
<div class="post">
|
||
<div class="post-header">
|
||
|
||
<div class="meta">
|
||
<div class="date">
|
||
<span class="day">17</span>
|
||
<span class="rest">Nov 2015</span>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="matter">
|
||
<h1 class="title">Spellchecking in retrieve and rank</h1>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="markdown">
|
||
<h3 id="introduction">Introduction</h3>
|
||
<p>Being able to deal with typos and incorrect spellings is an absolute must in any modern search facility. Humans can be lazy and clumsy and I personally often search for things with incorrect terms due to my sausage fingers. In this article I will explain how to turn on spelling suggestions in retrieve and rank so that if your users ask your system for something with a clumsy query, you can suggest spelling fixes for them so that they can submit another, more fruitful question to the system.</p>
|
||
<p>Spellchecking is a standard feature of Apache SOLR which is turned off by default with Retrieve and Rank. This post will walk through the process of turning it on for your instance and enabling spell checking suggestions to be returned as part of calls rankers through fcselect. Massive shout out to David Duffett on Stack Overflow who posted <a href="http://stackoverflow.com/questions/6653186/solr-suggester-not-returning-any-results">this answer</a> from which most of my blog post is derived.</p>
|
||
<h3 id="enabling-spell-checking-in-your-schema">Enabling spell checking in your schema</h3>
|
||
<p>The first thing we need to do is set up a spell checker field in our SOLR schema. For the sake of simplicity, the example schema used below only has a title and text field which are used in indexing and querying. However, this methodology can easily be extended to as many fields as your use case requires.</p>
|
||
<h3 id="set-up-field-type">Set up field type</h3>
|
||
<p>The first thing you need to do is define a “textSpell” field type which SOLR can use to build a field into which it can dump valid words from your corpus that have been preprocessed and made ready for use in the spell checker. Create the following element in <strong>your schema.xml</strong> file:</p>
|
||
<pre><fieldType name="textSpell" class="solr.TextField" positionIncrementGap="100" omitNorms="true">
|
||
<analyzer type="index">
|
||
<tokenizer class="solr.StandardTokenizerFactory" />
|
||
<filter class="solr.StopFilterFactory" ignoreCase="true" words="stopwords.txt" />
|
||
<filter class="solr.LowerCaseFilterFactory" />
|
||
<filter class="solr.StandardFilterFactory" />
|
||
</analyzer>
|
||
<analyzer type="query">
|
||
<tokenizer class="solr.StandardTokenizerFactory" />
|
||
<filter class="solr.SynonymFilterFactory" synonyms="synonyms.txt" ignoreCase="true" expand="true" />
|
||
<filter class="solr.StopFilterFactory" ignoreCase="true" words="stopwords.txt" />
|
||
<filter class="solr.LowerCaseFilterFactory" />
|
||
<filter class="solr.StandardFilterFactory" />
|
||
</analyzer>
|
||
</fieldType></pre>
|
||
<p>This field type runs a lower case filter over the words provided in the input and also expands any synonyms defined in synonyms.txt and ignores any stopwords defined in stopwords.txt before storing the output in the field. This should give us a list of lower case words that are useful in search and spell checking.</p>
|
||
<h3 id="create-a-spellcheck-copy-field-in-your-schema">Create a spellcheck copy field in your schema</h3>
|
||
<p>The next step is to create a “textSpell” field in your SOLR schema that stores the “suggestions” from the main content to be used by the spellchecker API.</p>
|
||
<p>The following XML defines the field in your schema and should be copied <strong>into schema.xml.</strong> It assumes that you have a couple of content fields called “title” and “text” from which content can be copied and filtered for use in the spell checker.</p>
|
||
<pre><field name="spell" type="textSpell" indexed="true" stored="false" multiValued="true" />
|
||
|
||
<copyField source="title" dest="spell"/>
|
||
<copyField source="text" dest="spell"/></pre>
|
||
<h3 id="defining-the-spellcheck-search-component">Defining the spellcheck search component</h3>
|
||
<p>Once you have finished setting up your schema, you can define the spellchecker parameters <strong>in solrconfig.xml.</strong></p>
|
||
<p>The following XML defines two spelling analysers. The DirectSolrSpellChecker which pulls search terms directly from the index adhoc – this means that it does not need to be regularly reindexed/rebuilt and always has up to date spelling suggestions.</p>
|
||
<p><a href="https://cwiki.apache.org/confluence/display/solr/Spell+Checking"><code>WordBreakSolrSpellChecker</code> offers suggestions by combining adjacent query terms and/or breaking terms into multiple words.</a> This means that it can provide suggestions that DirectSolrSpellChecker might not find where, for example, a user has a spelling mistake in one of the words in a multi-word search term.</p>
|
||
<p>Notice that both lst elements contain a <str name=”field”>spell</str> attribute. This must map to at the spell field we defined in the above step so if you used a different name for your field, substitute this in here.</p>
|
||
<p><a href="https://cwiki.apache.org/confluence/display/solr/Spell+Checking">The documentation</a> provides more detail on how to configure the individual spell check components as well as some alternatives to Direct and Wordbreak which might be more useful depending on your own use case. <strong>Your mileage may vary.</strong></p>
|
||
<pre><searchComponent name="spellcheck" class="solr.SpellCheckComponent">
|
||
<lst name="spellchecker">
|
||
<str name="name">default</str>
|
||
<str name="field">spell</str>
|
||
<str name="classname">solr.DirectSolrSpellChecker</str>
|
||
<str name="distanceMeasure">internal</str>
|
||
<float name="accuracy">0.5</float>
|
||
<int name="maxEdits">2</int>
|
||
<int name="minPrefix">1</int>
|
||
<int name="maxInspections">5</int>
|
||
<int name="minQueryLength">4</int>
|
||
<float name="maxQueryFrequency">0.01</float>
|
||
<float name="thresholdTokenFrequency">.01</float>
|
||
</lst>
|
||
|
||
<lst name="spellchecker">
|
||
<str name="name">wordbreak</str>
|
||
<str name="classname">solr.WordBreakSolrSpellChecker</str>
|
||
<str name="field">spell</str>
|
||
<str name="combineWords">true</str>
|
||
<str name="breakWords">true</str>
|
||
<int name="maxChanges">10</int>
|
||
</lst>
|
||
</searchComponent></pre>
|
||
<h3 id="add-spelling-suggestions-to-your-request-handlers">Add spelling suggestions to your request handlers</h3>
|
||
<p>The default SOLR approach is to add a new request handler that deals with searches on the <strong>/spell</strong> endpoint. However, there is no reason why you can’t add spelling suggestions to any endpoint including <strong>/select</strong> and perhaps more relevently in retrieve and rank <strong>/fcselect</strong>. Below is a snippet of XML for a custom /spell endpoint:</p>
|
||
<pre><requestHandler name="/spell" class="solr.SearchHandler" startup="lazy">
|
||
<lst name="defaults">
|
||
<!-- Solr will use suggestions from both the 'default' spellchecker
|
||
and from the 'wordbreak' spellchecker and combine them.
|
||
collations (re-written queries) can include a combination of
|
||
corrections from both spellcheckers -->
|
||
<str name="spellcheck.dictionary">default</str>
|
||
<str name="spellcheck.dictionary">wordbreak</str>
|
||
<str name="spellcheck">on</str>
|
||
<str name="spellcheck.extendedResults">true</str>
|
||
<str name="spellcheck.count">10</str>
|
||
<str name="spellcheck.alternativeTermCount">5</str>
|
||
<str name="spellcheck.maxResultsForSuggest">5</str>
|
||
<str name="spellcheck.collate">true</str>
|
||
<str name="spellcheck.collateExtendedResults">true</str>
|
||
<str name="spellcheck.maxCollationTries">10</str>
|
||
<str name="spellcheck.maxCollations">5</str>
|
||
</lst>
|
||
<arr name="last-components">
|
||
<str>spellcheck</str>
|
||
</arr>
|
||
</requestHandler></pre>
|
||
<p>The following snippet adds spellchecking suggestions to the <strong>/fcselect</strong> endpoint. Simply append the XML inside the _**<requestHandler name=”/fcselect” class=”com.ibm.watson.hector.plugins.ss.FCSearchHandler”></requestHandler> **_markup area.</p>
|
||
<pre><requestHandler name="/fcselect" class="com.ibm.watson.hector.plugins.ss.FCSearchHandler">
|
||
<lst name="defaults">
|
||
<str name="defType">fcQueryParser</str>
|
||
<str name="spellcheck.dictionary">default</str>
|
||
<str name="spellcheck.dictionary">wordbreak</str>
|
||
<str name="spellcheck.count">20</str>
|
||
</lst>
|
||
<arr name="last-components">
|
||
<str>fcFeatureGenerator</str>
|
||
<str>spellcheck</str>
|
||
</arr>
|
||
</requestHandler></pre>
|
||
<h3 id="create-and-populate-your-solr-index-in-retrieve-and-rank">Create and populate your SOLR index in Retrieve and Rank</h3>
|
||
<p>If you haven’t done this before, you should really read the <a href="http://www.ibm.com/smarterplanet/us/en/ibmwatson/developercloud/doc/retrieve-rank/get_start.shtml">official documentation</a> and may want to read <a href="https://brainsteam.co.uk/2015/11/16/retrieve-and-rank-and-python/">my post about using python to do it too.</a></p>
|
||
<p>You should also <a href="https://www.ibm.com/smarterplanet/us/en/ibmwatson/developercloud/doc/retrieve-rank/plugin_overview.shtml#generate_queries">train a ranker</a> so that you can take advantage of the fcselect with spelling suggestions example below.</p>
|
||
<h3 id="test-your-new-spelling-suggestor">Test your new spelling suggestor</h3>
|
||
<p>Once you’ve got your collection up and running you should be able to try out the new spelling suggestor. First we’ll inspect <strong>/spell:</strong></p>
|
||
<pre>$ curl -u $USER:$PASSWORD "https://gateway.watsonplatform.net/retrieve-and-rank/api/v1/solr_clusters/$CLUSTER_ID/solr/$COLLECTION_NAME/spell?q=businwss&wt=json
|
||
|
||
{"responseHeader":{"status":0,"QTime":4},"response":{"numFound":0,"start":0,"docs":[]},"spellcheck":{"suggestions":["businwss",{"numFound":1,"startOffset":0,"endOffset":8,"origFreq":0,"suggestion":[{"word":"business","freq":3}]}],"correctlySpelled":false,"collations":["collation",{"collationQuery":"business","hits":3,"misspellingsAndCorrections":["businwss","business"]}]}}
|
||
|
||
</pre>
|
||
<p>As you can see, the system has not found any documents containing the word **businwss. **However, it has identified **businwss **(easily misspelt because ‘e’ and ‘w’ are next to each other) as a typo of <strong>business</strong>. It has also suggested business as a correction. This can be presented back to the user so that they can refine their search and presented with more results.</p>
|
||
<p>Now lets also look at how to use spellcheck with your ranker results.</p>
|
||
<pre>$ curl -u $USER:$PASSWORD "https://gateway.watsonplatform.net/retrieve-and-rank/api/v1/solr_clusters/$CLUSTER_ID/solr/$COLLECTION_NAME/fcselect?ranker_id=$RANKER_ID&q=test+splling+mstaek&wt=json&fl=id,title,score&spellcheck=true&spellcheck=true"
|
||
|
||
{"responseHeader":{"status":0,"QTime":4},"response":{"numFound":0,"start":0,"maxScore":0.0,"docs":[]},"spellcheck":{"suggestions":["businwss",{"numFound":1,"startOffset":0,"endOffset":8,"suggestion":["business"]}]}}</pre>
|
||
<p>You should see something similar to the above. The SOLR search failed to return any results for the ranker to rank. However it has come up with a spelling correction which should return more results for ranking next time.</p>
|
||
|
||
</div>
|
||
|
||
<div class="tags">
|
||
|
||
|
||
<ul class="flat">
|
||
|
||
<li><a href="/tags/checker">checker</a></li>
|
||
|
||
<li><a href="/tags/improvements">improvements</a></li>
|
||
|
||
<li><a href="/tags/rank">rank</a></li>
|
||
|
||
<li><a href="/tags/retrieve">retrieve</a></li>
|
||
|
||
<li><a href="/tags/search">search</a></li>
|
||
|
||
<li><a href="/tags/solr">solr</a></li>
|
||
|
||
<li><a href="/tags/spell">spell</a></li>
|
||
|
||
<li><a href="/tags/spelling">spelling</a></li>
|
||
|
||
<li><a href="/tags/suggestions">suggestions</a></li>
|
||
|
||
<li><a href="/tags/tuning">tuning</a></li>
|
||
|
||
<li><a href="/tags/watson">watson</a></li>
|
||
|
||
</ul>
|
||
|
||
|
||
</div><div id="disqus_thread"></div>
|
||
<script type="text/javascript">
|
||
(function () {
|
||
|
||
|
||
if (window.location.hostname == "localhost")
|
||
return;
|
||
|
||
var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true;
|
||
var disqus_shortname = 'brainsteam';
|
||
dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js';
|
||
(document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq);
|
||
})();
|
||
</script>
|
||
<noscript>Please enable JavaScript to view the </a></noscript>
|
||
<a href="http://disqus.com/" class="dsq-brlink">comments powered by <span class="logo-disqus">Disqus</span></a>
|
||
</div>
|
||
</div>
|
||
<div class="footer wrapper">
|
||
<nav class="nav">
|
||
<div>2021 © James Ravenscroft 2020 | <a href="https://github.com/knadh/hugo-ink">Ink</a> theme on <a href="https://gohugo.io">Hugo</a></div>
|
||
</nav>
|
||
</div>
|
||
|
||
|
||
<script type="application/javascript">
|
||
var doNotTrack = false;
|
||
if (!doNotTrack) {
|
||
window.ga=window.ga||function(){(ga.q=ga.q||[]).push(arguments)};ga.l=+new Date;
|
||
ga('create', 'UA-186263385-1', 'auto');
|
||
|
||
ga('send', 'pageview');
|
||
}
|
||
</script>
|
||
<script async src='https://www.google-analytics.com/analytics.js'></script>
|
||
<script>feather.replace()</script>
|
||
</body>
|
||
</html>
|