diff --git a/CHANGES.txt b/CHANGES.txt index 0f62f0ec960..8ca6fb5da56 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -336,6 +336,10 @@ New Features data sources, processors and transformers for importing data. Supports full data imports as well as incremental (delta) indexing. See http://wiki.apache.org/solr/DataImportHandler for more details. (Noble Paul, shalin) + +67. SOLR-622: SpellCheckComponent supports auto-loading indices on startup and optionally, (re)builds indices + on newSearcher event, if configured in solrconfig.xml + (shalin) Changes in runtime behavior 1. SOLR-559: use Lucene updateDocument, deleteDocuments methods. This diff --git a/src/java/org/apache/solr/handler/component/SpellCheckComponent.java b/src/java/org/apache/solr/handler/component/SpellCheckComponent.java index a06ef452d7e..6664bdc64fc 100644 --- a/src/java/org/apache/solr/handler/component/SpellCheckComponent.java +++ b/src/java/org/apache/solr/handler/component/SpellCheckComponent.java @@ -17,10 +17,24 @@ package org.apache.solr.handler.component; -import org.apache.lucene.analysis.Token; -import org.apache.lucene.analysis.WhitespaceAnalyzer; +import java.io.IOException; +import java.io.StringReader; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javax.xml.xpath.XPathConstants; + import org.apache.lucene.analysis.Analyzer; +import org.apache.lucene.analysis.Token; import org.apache.lucene.analysis.TokenStream; +import org.apache.lucene.analysis.WhitespaceAnalyzer; import org.apache.lucene.index.IndexReader; import org.apache.solr.common.SolrException; import org.apache.solr.common.params.CommonParams; @@ -30,29 +44,21 @@ import org.apache.solr.common.util.NamedList; import org.apache.solr.common.util.SimpleOrderedMap; import org.apache.solr.core.SolrConfig; import org.apache.solr.core.SolrCore; +import org.apache.solr.core.SolrEventListener; import org.apache.solr.core.SolrResourceLoader; import org.apache.solr.schema.FieldType; import org.apache.solr.schema.IndexSchema; +import org.apache.solr.search.SolrIndexSearcher; +import org.apache.solr.spelling.AbstractLuceneSpellChecker; import org.apache.solr.spelling.IndexBasedSpellChecker; +import org.apache.solr.spelling.QueryConverter; import org.apache.solr.spelling.SolrSpellChecker; import org.apache.solr.spelling.SpellingResult; -import org.apache.solr.spelling.QueryConverter; +import org.apache.solr.util.RefCounted; import org.apache.solr.util.plugin.NamedListPluginLoader; import org.apache.solr.util.plugin.SolrCoreAware; import org.w3c.dom.NodeList; -import javax.xml.xpath.XPathConstants; -import java.io.IOException; -import java.io.StringReader; -import java.util.Collection; -import java.util.HashMap; -import java.util.Map; -import java.util.ArrayList; -import java.util.LinkedHashMap; -import java.util.Iterator; -import java.util.concurrent.ConcurrentHashMap; -import java.util.logging.Logger; - /** * A SearchComponent implementation which provides support for spell checking * and suggestions using the Lucene contributed SpellChecker. @@ -263,6 +269,12 @@ public class SpellCheckComponent extends SearchComponent implements SolrCoreAwar throw new RuntimeException("More than one dictionary is missing name."); } } + // Register event listeners for this SpellChecker + core.registerFirstSearcherListener(new SpellCheckerListener(core, checker, true)); + if (Boolean.parseBoolean((String)spellchecker.get("buildOnCommit"))) { + LOG.info("Registering newSearcher listener for spellchecker: " + checker.getDictionaryName()); + core.registerNewSearcherListener(new SpellCheckerListener(core, checker, false)); + } } else { throw new RuntimeException("Can't load spell checker: " + className); } @@ -294,6 +306,47 @@ public class SpellCheckComponent extends SearchComponent implements SolrCoreAwar } } + private static class SpellCheckerListener implements SolrEventListener { + private final SolrCore core; + private final SolrSpellChecker checker; + private final boolean firstSearcher; + + public SpellCheckerListener(SolrCore core, SolrSpellChecker checker, boolean firstSearcher) { + this.core = core; + this.checker = checker; + this.firstSearcher = firstSearcher; + } + + public void init(NamedList args) { + } + + public void newSearcher(SolrIndexSearcher newSearcher, + SolrIndexSearcher currentSearcher) { + if (firstSearcher) { + try { + LOG.info("Loading spell index for spellchecker: " + + checker.getDictionaryName()); + checker.reload(); + } catch (IOException e) { + LOG.log(Level.SEVERE, "Exception in reloading spell check index for spellchecker: " + checker.getDictionaryName(), e); + } + } else { + // newSearcher event + try { + LOG.info("Building spell index for spell checker: " + checker.getDictionaryName()); + checker.build(core, newSearcher); + } catch (Exception e) { + LOG.log(Level.SEVERE, + "Exception in building spell check index for spellchecker: " + checker.getDictionaryName(), e); + } + } + + } + + public void postCommit() { + } + } + // /////////////////////////////////////////// // / SolrInfoMBean // ////////////////////////////////////////// diff --git a/src/test/org/apache/solr/handler/component/SpellCheckComponentTest.java b/src/test/org/apache/solr/handler/component/SpellCheckComponentTest.java index c15845e33a5..0b11a4e3e19 100644 --- a/src/test/org/apache/solr/handler/component/SpellCheckComponentTest.java +++ b/src/test/org/apache/solr/handler/component/SpellCheckComponentTest.java @@ -67,7 +67,7 @@ public class SpellCheckComponentTest extends AbstractSolrTestCase { assertU(adoc("id", "8", "lowerfilt", "blee")); assertU("commit", commit()); } - + public void testExtendedResultsCount() throws Exception { SolrCore core = h.getCore(); SearchComponent speller = core.getSearchComponent("spellcheck"); @@ -348,7 +348,19 @@ public class SpellCheckComponentTest extends AbstractSolrTestCase { fail("NullPointerException due to reload not initializing analyzers"); } } - + + @SuppressWarnings("unchecked") + public void testRebuildOnCommit() throws Exception { + SolrQueryRequest req = req("q", "lowerfilt:lucenejavt", "qt", "spellCheckCompRH", "spellcheck", "true"); + String response = h.query(req); + assertFalse("No suggestions should be returned", response.contains("lucenejava")); + + assertU(adoc("id", "11231", "lowerfilt", "lucenejava")); + assertU("commit", commit()); + + assertQ(req, "//arr[@name='suggestion'][.='lucenejava']"); + } + // TODO: add more tests for various spelling options } diff --git a/src/test/test-files/solr/conf/solrconfig.xml b/src/test/test-files/solr/conf/solrconfig.xml index 723109e5b5a..4b0b94654a8 100644 --- a/src/test/test-files/solr/conf/solrconfig.xml +++ b/src/test/test-files/solr/conf/solrconfig.xml @@ -326,6 +326,7 @@ default lowerfilt spellchecker1 + true