Index: solr/src/test/org/apache/solr/spelling/DirectSolrSpellCheckerTest.java
===================================================================
--- solr/src/test/org/apache/solr/spelling/DirectSolrSpellCheckerTest.java (revision 0)
+++ solr/src/test/org/apache/solr/spelling/DirectSolrSpellCheckerTest.java (revision 0)
@@ -0,0 +1,78 @@
+package org.apache.solr.spelling;
+
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.util.Collection;
+import java.util.Map;
+
+import org.apache.lucene.analysis.Token;
+import org.apache.lucene.index.IndexReader;
+import org.apache.solr.SolrTestCaseJ4;
+import org.apache.solr.common.util.NamedList;
+import org.apache.solr.core.SolrCore;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+/**
+ * Simple tests for {@link DirectSolrSpellChecker}
+ */
+public class DirectSolrSpellCheckerTest extends SolrTestCaseJ4 {
+
+ private static SpellingQueryConverter queryConverter;
+
+ @BeforeClass
+ public static void beforeClass() throws Exception {
+ initCore("solrconfig.xml","schema.xml");
+ //Index something with a title
+ assertNull(h.validateUpdate(adoc("id", "0", "teststop", "This is a title")));
+ assertNull(h.validateUpdate(adoc("id", "1", "teststop", "The quick reb fox jumped over the lazy brown dogs.")));
+ assertNull(h.validateUpdate(adoc("id", "2", "teststop", "This is a Solr")));
+ assertNull(h.validateUpdate(adoc("id", "3", "teststop", "solr foo")));
+ assertNull(h.validateUpdate(commit()));
+ queryConverter = new SimpleQueryConverter();
+ queryConverter.init(new NamedList());
+ }
+
+ @Test
+ public void test() throws Exception {
+ DirectSolrSpellChecker checker = new DirectSolrSpellChecker();
+ NamedList spellchecker = new NamedList();
+ spellchecker.add("classname", DirectSolrSpellChecker.class.getName());
+ spellchecker.add(DirectSolrSpellChecker.FIELD, "teststop");
+ spellchecker.add(DirectSolrSpellChecker.MINQUERYLENGTH, "2"); // we will try "fob"
+
+ SolrCore core = h.getCore();
+ checker.init(spellchecker, core);
+
+ IndexReader reader = core.getSearcher().get().getReader();
+ Collection tokens = queryConverter.convert("fob");
+ SpellingOptions spellOpts = new SpellingOptions(tokens, reader);
+ SpellingResult result = checker.getSuggestions(spellOpts);
+ assertTrue("result is null and it shouldn't be", result != null);
+ Map suggestions = result.get(tokens.iterator().next());
+ Map.Entry entry = suggestions.entrySet().iterator().next();
+ assertTrue(entry.getKey() + " is not equal to " + "foo", entry.getKey().equals("foo") == true);
+ assertFalse(entry.getValue() + " equals: " + SpellingResult.NO_FREQUENCY_INFO, entry.getValue() == SpellingResult.NO_FREQUENCY_INFO);
+
+ spellOpts.tokens = queryConverter.convert("super");
+ result = checker.getSuggestions(spellOpts);
+ assertTrue("result is null and it shouldn't be", result != null);
+ suggestions = result.get(tokens.iterator().next());
+ assertTrue("suggestions is not null and it should be", suggestions == null);
+ }
+}
Property changes on: solr\src\test\org\apache\solr\spelling\DirectSolrSpellCheckerTest.java
___________________________________________________________________
Added: svn:eol-style
+ native
Index: solr/src/java/org/apache/solr/spelling/DirectSolrSpellChecker.java
===================================================================
--- solr/src/java/org/apache/solr/spelling/DirectSolrSpellChecker.java (revision 0)
+++ solr/src/java/org/apache/solr/spelling/DirectSolrSpellChecker.java (revision 0)
@@ -0,0 +1,191 @@
+package org.apache.solr.spelling;
+
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.io.IOException;
+import java.util.Comparator;
+
+import org.apache.lucene.analysis.Token;
+import org.apache.lucene.index.Term;
+import org.apache.lucene.search.spell.DirectSpellChecker;
+import org.apache.lucene.search.spell.StringDistance;
+import org.apache.lucene.search.spell.SuggestWord;
+import org.apache.lucene.search.spell.SuggestWordFrequencyComparator;
+import org.apache.lucene.search.spell.SuggestWordQueue;
+import org.apache.solr.common.util.NamedList;
+import org.apache.solr.core.SolrCore;
+import org.apache.solr.search.SolrIndexSearcher;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Spellchecker implementation that uses {@link DirectSpellChecker}
+ *
+ * Requires no auxiliary index or data structure.
+ *
+ * Supported options:
+ *
+ * - field: Used as the source of terms.
+ *
- distanceMeasure: Sets {@link DirectSpellChecker#setDistance(StringDistance)}.
+ * Note: to set the default {@link DirectSpellChecker#INTERNAL_LEVENSHTEIN}, use "internal".
+ *
- accuracy: Sets {@link DirectSpellChecker#setAccuracy(float)}.
+ *
- maxEdits: Sets {@link DirectSpellChecker#setMaxEdits(int)}.
+ *
- minPrefix: Sets {@link DirectSpellChecker#setMinPrefix(int)}.
+ *
- maxInspections: Sets {@link DirectSpellChecker#setMaxInspections(int)}.
+ *
- comparatorClass: Sets {@link DirectSpellChecker#setComparator(Comparator)}.
+ * Note: score-then-frequency can be specified as "score" and frequency-then-score
+ * can be specified as "freq".
+ *
- thresholdTokenFrequency: sets {@link DirectSpellChecker#setThresholdFrequency(float)}.
+ *
- minQueryLength: sets {@link DirectSpellChecker#setMinQueryLength(int)}.
+ *
- maxQueryFrequency: sets {@link DirectSpellChecker#setMaxQueryFrequency(float)}.
+ *
+ * @see DirectSpellChecker
+ */
+public class DirectSolrSpellChecker extends SolrSpellChecker {
+ private static final Logger LOG = LoggerFactory.getLogger(DirectSolrSpellChecker.class);
+
+ /** Field to use as the source of terms */
+ public static final String FIELD = "field";
+
+ public static final String STRING_DISTANCE = "distanceMeasure";
+ public static final String INTERNAL_DISTANCE = "internal";
+
+ public static final String ACCURACY = "accuracy";
+ public static final float DEFAULT_ACCURACY = 0.5f;
+
+ public static final String MAXEDITS = "maxEdits";
+ public static final int DEFAULT_MAXEDITS = 2;
+
+ public static final String MINPREFIX = "minPrefix";
+ public static final int DEFAULT_MINPREFIX = 1;
+
+ public static final String MAXINSPECTIONS = "maxInspections";
+ public static final int DEFAULT_MAXINSPECTIONS = 5;
+
+ public static final String COMPARATOR_CLASS = "comparatorClass";
+ public static final String SCORE_COMP = "score";
+ public static final String FREQ_COMP = "freq";
+
+ public static final String THRESHOLD = "thresholdTokenFrequency";
+ public static final float DEFAULT_THRESHOLD = 0.0f;
+
+ public static final String MINQUERYLENGTH = "minQueryLength";
+ public static final int DEFAULT_MINQUERYLENGTH = 4;
+
+ public static final String MAXQUERYFREQUENCY = "maxQueryFrequency";
+ public static final float DEFAULT_MAXQUERYFREQUENCY = 0.01f;
+
+ private DirectSpellChecker checker = new DirectSpellChecker();
+ private String field;
+
+ @Override
+ public String init(NamedList config, SolrCore core) {
+ LOG.info("init: " + config);
+ String name = super.init(config, core);
+
+ Comparator comp = SuggestWordQueue.DEFAULT_COMPARATOR;
+ String compClass = (String) config.get(COMPARATOR_CLASS);
+ if (compClass != null) {
+ if (compClass.equalsIgnoreCase(SCORE_COMP))
+ comp = SuggestWordQueue.DEFAULT_COMPARATOR;
+ else if (compClass.equalsIgnoreCase(FREQ_COMP))
+ comp = new SuggestWordFrequencyComparator();
+ else //must be a FQCN
+ comp = (Comparator) core.getResourceLoader().newInstance(compClass);
+ }
+
+ StringDistance sd = DirectSpellChecker.INTERNAL_LEVENSHTEIN;
+ String distClass = (String) config.get(STRING_DISTANCE);
+ if (distClass != null && !distClass.equalsIgnoreCase(INTERNAL_DISTANCE))
+ sd = (StringDistance) core.getResourceLoader().newInstance(distClass);
+
+ field = (String) config.get(FIELD);
+
+ float minAccuracy = DEFAULT_ACCURACY;
+ String accuracy = (String) config.get(ACCURACY);
+ if (accuracy != null)
+ minAccuracy = Float.parseFloat(accuracy);
+
+ int maxEdits = DEFAULT_MAXEDITS;
+ String edits = (String) config.get(MAXEDITS);
+ if (edits != null)
+ maxEdits = Integer.parseInt(edits);
+
+ int minPrefix = DEFAULT_MINPREFIX;
+ String prefix = (String) config.get(MINPREFIX);
+ if (prefix != null)
+ minPrefix = Integer.parseInt(prefix);
+
+ int maxInspections = DEFAULT_MAXINSPECTIONS;
+ String inspections = (String) config.get(MAXINSPECTIONS);
+ if (inspections != null)
+ maxInspections = Integer.parseInt(inspections);
+
+ float minThreshold = DEFAULT_THRESHOLD;
+ String threshold = (String) config.get(THRESHOLD);
+ if (threshold != null)
+ minThreshold = Float.parseFloat(threshold);
+
+ int minQueryLength = DEFAULT_MINQUERYLENGTH;
+ String queryLength = (String) config.get(MINQUERYLENGTH);
+ if (queryLength != null)
+ minQueryLength = Integer.parseInt(queryLength);
+
+ float maxQueryFrequency = DEFAULT_MAXQUERYFREQUENCY;
+ String queryFreq = (String) config.get(MAXQUERYFREQUENCY);
+ if (queryFreq != null)
+ maxQueryFrequency = Float.parseFloat(queryFreq);
+
+ checker.setComparator(comp);
+ checker.setDistance(sd);
+ checker.setMaxEdits(maxEdits);
+ checker.setMinPrefix(minPrefix);
+ checker.setAccuracy(minAccuracy);
+ checker.setThresholdFrequency(minThreshold);
+ checker.setMaxInspections(maxInspections);
+ checker.setMinQueryLength(minQueryLength);
+ checker.setMaxQueryFrequency(maxQueryFrequency);
+ checker.setLowerCaseTerms(false);
+
+ return name;
+ }
+
+ @Override
+ public void reload(SolrCore core, SolrIndexSearcher searcher)
+ throws IOException {}
+
+ @Override
+ public void build(SolrCore core, SolrIndexSearcher searcher) {}
+
+ @Override
+ public SpellingResult getSuggestions(SpellingOptions options)
+ throws IOException {
+ LOG.debug("getSuggestions: " + options.tokens);
+
+ SpellingResult result = new SpellingResult();
+ float accuracy = (options.accuracy == Float.MIN_VALUE) ? checker.getAccuracy() : options.accuracy;
+
+ for (Token token : options.tokens) {
+ SuggestWord[] suggestions = checker.suggestSimilar(new Term(field, token.toString()),
+ options.count, options.reader, options.onlyMorePopular, accuracy);
+ for (SuggestWord suggestion : suggestions)
+ result.add(token, suggestion.string, suggestion.freq);
+ }
+ return result;
+ }
+}
Property changes on: solr\src\java\org\apache\solr\spelling\DirectSolrSpellChecker.java
___________________________________________________________________
Added: svn:eol-style
+ native
Index: solr/example/solr/conf/solrconfig.xml
===================================================================
--- solr/example/solr/conf/solrconfig.xml (revision 1002256)
+++ solr/example/solr/conf/solrconfig.xml (working copy)
@@ -708,6 +708,18 @@
./spellchecker
+