You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by jd...@apache.org on 2012/06/04 19:55:26 UTC

svn commit: r1346058 [2/2] - in /lucene/dev/trunk/solr: ./ core/src/java/org/apache/solr/handler/component/ core/src/java/org/apache/solr/spelling/ core/src/test-files/solr/conf/ core/src/test/org/apache/solr/handler/component/ core/src/test/org/apache...

Modified: lucene/dev/trunk/solr/core/src/test/org/apache/solr/spelling/SpellingQueryConverterTest.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/core/src/test/org/apache/solr/spelling/SpellingQueryConverterTest.java?rev=1346058&r1=1346057&r2=1346058&view=diff
==============================================================================
--- lucene/dev/trunk/solr/core/src/test/org/apache/solr/spelling/SpellingQueryConverterTest.java (original)
+++ lucene/dev/trunk/solr/core/src/test/org/apache/solr/spelling/SpellingQueryConverterTest.java Mon Jun  4 17:55:25 2012
@@ -23,7 +23,9 @@ import org.apache.lucene.util.LuceneTest
 import org.apache.solr.common.util.NamedList;
 import org.junit.Test;
 
+import java.util.ArrayList;
 import java.util.Collection;
+import java.util.List;
 
 
 /**
@@ -126,4 +128,75 @@ public class SpellingQueryConverterTest 
     assertTrue("tokens is null and it shouldn't be", tokens != null);
     assertEquals("tokens Size: " + tokens.size() + " is not 2", 2, tokens.size());
   }
+  
+  @Test
+  public void testRequiredOrProhibitedFlags() {
+    SpellingQueryConverter converter = new SpellingQueryConverter();
+    converter.init(new NamedList());
+    converter.setAnalyzer(new WhitespaceAnalyzer(TEST_VERSION_CURRENT));
+
+    {
+      List<Token> tokens = new ArrayList<Token>(converter.convert("aaa bbb ccc"));
+      assertTrue("Should have 3 tokens",          tokens != null && tokens.size()==3);
+      assertTrue("token 1 should be optional",    !hasRequiredFlag(tokens.get(0)) && !hasProhibitedFlag(tokens.get(0)));
+      assertTrue("token 2 should be optional",    !hasRequiredFlag(tokens.get(1)) && !hasProhibitedFlag(tokens.get(1)));
+      assertTrue("token 3 should be optional",    !hasRequiredFlag(tokens.get(2)) && !hasProhibitedFlag(tokens.get(2)));
+    }
+    {
+      List<Token> tokens = new ArrayList<Token>(converter.convert("+aaa bbb -ccc"));
+      assertTrue("Should have 3 tokens",          tokens != null && tokens.size()==3);
+      assertTrue("token 1 should be required",     hasRequiredFlag(tokens.get(0)) && !hasProhibitedFlag(tokens.get(0)));
+      assertTrue("token 2 should be optional",    !hasRequiredFlag(tokens.get(1)) && !hasProhibitedFlag(tokens.get(1)));
+      assertTrue("token 3 should be prohibited",  !hasRequiredFlag(tokens.get(2)) &&  hasProhibitedFlag(tokens.get(2)));
+    }
+    {
+      List<Token> tokens = new ArrayList<Token>(converter.convert("aaa AND bbb ccc"));
+      assertTrue("Should have 3 tokens",           tokens != null && tokens.size()==3);
+      assertTrue("token 1 doesn't precede n.b.o.",  !hasNBOFlag(tokens.get(0)) && hasInBooleanFlag(tokens.get(0)));
+      assertTrue("token 2 doesn't precede n.b.o.",  !hasNBOFlag(tokens.get(1)) && hasInBooleanFlag(tokens.get(0)));
+      assertTrue("token 3 doesn't precede n.b.o.",  !hasNBOFlag(tokens.get(2)) && hasInBooleanFlag(tokens.get(0)));
+    }
+    {
+      List<Token> tokens = new ArrayList<Token>(converter.convert("aaa OR bbb OR ccc"));
+      assertTrue("Should have 3 tokens",           tokens != null && tokens.size()==3);
+      assertTrue("token 1 doesn't precede n.b.o.",  !hasNBOFlag(tokens.get(0)) && hasInBooleanFlag(tokens.get(0)));
+      assertTrue("token 2 doesn't precede n.b.o.",  !hasNBOFlag(tokens.get(1)) && hasInBooleanFlag(tokens.get(0)));
+      assertTrue("token 3 doesn't precede n.b.o.",  !hasNBOFlag(tokens.get(2)) && hasInBooleanFlag(tokens.get(0)));
+    }
+    {
+      List<Token> tokens = new ArrayList<Token>(converter.convert("aaa AND bbb NOT ccc"));
+      assertTrue("Should have 3 tokens",            tokens != null && tokens.size()==3);
+      assertTrue("token 1 doesn't precede n.b.o.",  !hasNBOFlag(tokens.get(0)) && hasInBooleanFlag(tokens.get(0)));
+      assertTrue("token 2 precedes n.b.o.",          hasNBOFlag(tokens.get(1)) && hasInBooleanFlag(tokens.get(0)));
+      assertTrue("token 3 doesn't precede n.b.o.",  !hasNBOFlag(tokens.get(2)) && hasInBooleanFlag(tokens.get(0)));
+    }
+    {
+      List<Token> tokens = new ArrayList<Token>(converter.convert("aaa NOT bbb AND ccc"));
+      assertTrue("Should have 3 tokens",           tokens != null && tokens.size()==3);
+      assertTrue("token 1 precedes n.b.o.",          hasNBOFlag(tokens.get(0)) && hasInBooleanFlag(tokens.get(0)));
+      assertTrue("token 2 precedes n.b.o.",          hasNBOFlag(tokens.get(1)) && hasInBooleanFlag(tokens.get(0)));
+      assertTrue("token 3 doesn't precedes n.b.o.", !hasNBOFlag(tokens.get(2)) && hasInBooleanFlag(tokens.get(0)));
+    }
+    {
+      List<Token> tokens = new ArrayList<Token>(converter.convert("aaa AND NOT bbb AND ccc"));
+      assertTrue("Should have 3 tokens",           tokens != null && tokens.size()==3);
+      assertTrue("token 1 precedes n.b.o.",          hasNBOFlag(tokens.get(0)) && hasInBooleanFlag(tokens.get(0)));
+      assertTrue("token 2 precedes n.b.o.",          hasNBOFlag(tokens.get(1)) && hasInBooleanFlag(tokens.get(0)));
+      assertTrue("token 3 doesn't precedes n.b.o.", !hasNBOFlag(tokens.get(2)) && hasInBooleanFlag(tokens.get(0)));
+    }
+    
+  }
+  
+  private boolean hasRequiredFlag(Token t) {
+    return (t.getFlags() & QueryConverter.REQUIRED_TERM_FLAG) == QueryConverter.REQUIRED_TERM_FLAG;
+  }
+  private boolean hasProhibitedFlag(Token t) {
+    return (t.getFlags() & QueryConverter.PROHIBITED_TERM_FLAG) == QueryConverter.PROHIBITED_TERM_FLAG;
+  }
+  private boolean hasNBOFlag(Token t) {
+    return (t.getFlags() & QueryConverter.TERM_PRECEDES_NEW_BOOLEAN_OPERATOR_FLAG) == QueryConverter.TERM_PRECEDES_NEW_BOOLEAN_OPERATOR_FLAG;
+  }
+  private boolean hasInBooleanFlag(Token t) {
+    return (t.getFlags() & QueryConverter.TERM_IN_BOOLEAN_QUERY_FLAG) == QueryConverter.TERM_IN_BOOLEAN_QUERY_FLAG;
+  }
 }

Added: lucene/dev/trunk/solr/core/src/test/org/apache/solr/spelling/WordBreakSolrSpellCheckerTest.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/core/src/test/org/apache/solr/spelling/WordBreakSolrSpellCheckerTest.java?rev=1346058&view=auto
==============================================================================
--- lucene/dev/trunk/solr/core/src/test/org/apache/solr/spelling/WordBreakSolrSpellCheckerTest.java (added)
+++ lucene/dev/trunk/solr/core/src/test/org/apache/solr/spelling/WordBreakSolrSpellCheckerTest.java Mon Jun  4 17:55:25 2012
@@ -0,0 +1,273 @@
+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.LinkedHashMap;
+import java.util.Map;
+
+import org.apache.lucene.analysis.MockAnalyzer;
+import org.apache.lucene.analysis.Token;
+import org.apache.solr.SolrTestCaseJ4;
+import org.apache.solr.common.util.NamedList;
+import org.apache.solr.core.SolrCore;
+import org.apache.solr.handler.component.SpellCheckComponent;
+import org.apache.solr.search.SolrIndexSearcher;
+import org.apache.solr.util.RefCounted;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+public class WordBreakSolrSpellCheckerTest extends SolrTestCaseJ4 {
+  
+  @BeforeClass
+  public static void beforeClass() throws Exception {
+    initCore("solrconfig-spellcheckcomponent.xml","schema.xml");
+    assertNull(h.validateUpdate(adoc("id", "0", "lowerfilt", "pain table paintablepine pi ne in able")));
+    assertNull(h.validateUpdate(adoc("id", "1", "lowerfilt", "paint able pineapple goodness in")));
+    assertNull(h.validateUpdate(adoc("id", "2", "lowerfilt", "pa in table pineapplegoodness")));
+    assertNull(h.validateUpdate(adoc("id", "3", "lowerfilt", "printable line in ample food mess")));
+    assertNull(h.validateUpdate(adoc("id", "4", "lowerfilt", "printable in pointable paint able")));
+    assertNull(h.validateUpdate(adoc("id", "5", "lowerfilt", "printable in puntable paint able ")));
+    assertNull(h.validateUpdate(adoc("id", "6", "lowerfilt", "paint able in pintable plantable")));
+    assertNull(h.validateUpdate(commit()));    
+    //docfreq=7:  in
+    //docfreq=5:  able
+    //docfreq=4:  paint
+    //docfreq=3:  printable
+    //docfreq=2:  table
+    //docfreq=1:  {all others}
+  }
+  
+  @Test
+  public void testStandAlone() throws Exception {
+    SolrCore core = h.getCore();
+    WordBreakSolrSpellChecker checker = new WordBreakSolrSpellChecker();
+    NamedList<String> params = new NamedList<String>();
+    params.add("field", "lowerfilt");
+    params.add(WordBreakSolrSpellChecker.PARAM_BREAK_WORDS, "true");
+    params.add(WordBreakSolrSpellChecker.PARAM_COMBINE_WORDS, "true");
+    params.add(WordBreakSolrSpellChecker.PARAM_MAX_CHANGES, "10");
+    checker.init(params, core);
+
+    RefCounted<SolrIndexSearcher> searcher = core.getSearcher();
+    QueryConverter qc = new SpellingQueryConverter();
+    qc.setAnalyzer(new MockAnalyzer(random()));
+    Collection<Token> tokens = qc.convert("paintable pine apple good ness");
+    SpellingOptions spellOpts = new SpellingOptions(tokens, searcher.get().getIndexReader(), 10);
+    SpellingResult result = checker.getSuggestions(spellOpts);
+    searcher.decref();
+    
+    assertTrue(result != null && result.getSuggestions() != null);
+    assertTrue(result.getSuggestions().size()==6);
+    
+    for(Map.Entry<Token, LinkedHashMap<String, Integer>> s : result.getSuggestions().entrySet()) {
+      Token orig = s.getKey();
+      String[] corr = s.getValue().keySet().toArray(new String[0]);
+      if(orig.toString().equals("paintable")) {        
+        assertTrue(orig.startOffset()==0);
+        assertTrue(orig.endOffset()==9);
+        assertTrue(orig.length()==9);
+        assertTrue(corr.length==3);
+        assertTrue(corr[0].equals("paint able"));  //1 op ; max doc freq=5
+        assertTrue(corr[1].equals("pain table"));  //1 op ; max doc freq=2      
+        assertTrue(corr[2].equals("pa in table")); //2 ops
+      } else if(orig.toString().equals("pine apple")) {
+        assertTrue(orig.startOffset()==10);
+        assertTrue(orig.endOffset()==20);
+        assertTrue(orig.length()==10);
+        assertTrue(corr.length==1);
+        assertTrue(corr[0].equals("pineapple"));
+      } else if(orig.toString().equals("paintable pine")) {
+        assertTrue(orig.startOffset()==0);
+        assertTrue(orig.endOffset()==14);
+        assertTrue(orig.length()==14);
+        assertTrue(corr.length==1);
+        assertTrue(corr[0].equals("paintablepine"));
+      } else if(orig.toString().equals("good ness")) {
+        assertTrue(orig.startOffset()==21);
+        assertTrue(orig.endOffset()==30);
+        assertTrue(orig.length()==9);
+        assertTrue(corr.length==1);
+        assertTrue(corr[0].equals("goodness"));
+      } else if(orig.toString().equals("pine apple good ness")) {
+        assertTrue(orig.startOffset()==10);
+        assertTrue(orig.endOffset()==30);
+        assertTrue(orig.length()==20);
+        assertTrue(corr.length==1);
+        assertTrue(corr[0].equals("pineapplegoodness"));
+      } else if(orig.toString().equals("pine")) {
+        assertTrue(orig.startOffset()==10);
+        assertTrue(orig.endOffset()==14);
+        assertTrue(orig.length()==4);
+        assertTrue(corr.length==1);
+        assertTrue(corr[0].equals("pi ne"));
+      } else {
+        fail("Unexpected original result: " + orig);
+      }        
+    }  
+  }
+  @Test
+  public void testInConjunction() throws Exception {
+    assertQ(req(
+        "q", "lowerfilt:(paintable pine apple good ness)", 
+        "qt", "spellCheckWithWordbreak", 
+        "indent", "true",
+        SpellCheckComponent.SPELLCHECK_BUILD, "true",
+        SpellCheckComponent.COMPONENT_NAME, "true", 
+        SpellCheckComponent.SPELLCHECK_ACCURACY, ".75", 
+        SpellCheckComponent.SPELLCHECK_EXTENDED_RESULTS, "true"),
+        "//lst[@name='suggestions']/lst[1]/@name='paintable'",
+        "//lst[@name='suggestions']/lst[2]/@name='pine'",
+        "//lst[@name='suggestions']/lst[3]/@name='apple'",
+        "//lst[@name='suggestions']/lst[4]/@name='good'",
+        "//lst[@name='suggestions']/lst[5]/@name='ness'",
+        "//lst[@name='paintable']/int[@name='numFound']=8",
+        "//lst[@name='paintable']/int[@name='startOffset']=11",
+        "//lst[@name='paintable']/int[@name='endOffset']=20",
+        "//lst[@name='paintable']/arr[@name='suggestion']/str[1]='printable'",  //SolrSpellChecker result interleaved
+        "//lst[@name='paintable']/arr[@name='suggestion']/str[2]='paint able'", //1 op ; max doc freq=5 
+        "//lst[@name='paintable']/arr[@name='suggestion']/str[3]='pintable'",   //SolrSpellChecker result interleaved
+        "//lst[@name='paintable']/arr[@name='suggestion']/str[4]='pain table'", //1 op ; max doc freq=4 
+        "//lst[@name='paintable']/arr[@name='suggestion']/str[5]='pointable'",  //SolrSpellChecker result interleaved
+        "//lst[@name='paintable']/arr[@name='suggestion']/str[6]='pa in table'",//2 ops
+        "//lst[@name='paintable']/arr[@name='suggestion']/str[7]='plantable'",  //SolrSpellChecker result interleaved
+        "//lst[@name='paintable']/arr[@name='suggestion']/str[8]='puntable'",   //SolrSpellChecker result interleaved
+        "//lst[@name='pine']/int[@name='numFound']=2",
+        "//lst[@name='pine']/int[@name='startOffset']=21",
+        "//lst[@name='pine']/int[@name='endOffset']=25",
+        "//lst[@name='pine']/arr[@name='suggestion']/str[1]='line'",
+        "//lst[@name='pine']/arr[@name='suggestion']/str[2]='pi ne'",
+        "//lst[@name='apple']/int[@name='numFound']=1",
+        "//lst[@name='apple']/arr[@name='suggestion']/str[1]='ample'",
+        "//lst[@name='good']/int[@name='numFound']=1",
+        "//lst[@name='good']/arr[@name='suggestion']/str[1]='food'",
+        "//lst[@name='ness']/int[@name='numFound']=1",
+        "//lst[@name='ness']/arr[@name='suggestion']/str[1]='mess'",
+        "//lst[@name='pine apple']/int[@name='numFound']=1",
+        "//lst[@name='pine apple']/int[@name='startOffset']=21",
+        "//lst[@name='pine apple']/int[@name='endOffset']=31",
+        "//lst[@name='pine apple']/arr[@name='suggestion']/str[1]='pineapple'",
+        "//lst[@name='paintable pine']/int[@name='numFound']=1",
+        "//lst[@name='paintable pine']/int[@name='startOffset']=11",
+        "//lst[@name='paintable pine']/int[@name='endOffset']=25",
+        "//lst[@name='paintable pine']/arr[@name='suggestion']/str[1]='paintablepine'",
+        "//lst[@name='good ness']/int[@name='numFound']=1",
+        "//lst[@name='good ness']/int[@name='startOffset']=32",
+        "//lst[@name='good ness']/int[@name='endOffset']=41",
+        "//lst[@name='good ness']/arr[@name='suggestion']/str[1]='goodness'",
+        "//lst[@name='pine apple good ness']/int[@name='numFound']=1",
+        "//lst[@name='pine apple good ness']/int[@name='startOffset']=21",
+        "//lst[@name='pine apple good ness']/int[@name='endOffset']=41",
+        "//lst[@name='pine apple good ness']/arr[@name='suggestion']/str[1]='pineapplegoodness'"
+    );
+  }
+  @Test
+  public void testCollate() throws Exception {
+   assertQ(req(
+        "q", "lowerfilt:(paintable pine apple godness)", 
+        "qt", "spellCheckWithWordbreak", 
+        "indent", "true",
+        SpellCheckComponent.SPELLCHECK_BUILD, "true",
+        SpellCheckComponent.COMPONENT_NAME, "true", 
+        SpellCheckComponent.SPELLCHECK_ACCURACY, ".75", 
+        SpellCheckComponent.SPELLCHECK_EXTENDED_RESULTS, "true",
+        SpellCheckComponent.SPELLCHECK_COLLATE, "true",
+        SpellCheckComponent.SPELLCHECK_COLLATE_EXTENDED_RESULTS, "true",
+        SpellCheckComponent.SPELLCHECK_MAX_COLLATIONS, "10"),
+        "//lst[@name='collation'][1 ]/str[@name='collationQuery']='lowerfilt:(printable line ample goodness)'",
+        "//lst[@name='collation'][2 ]/str[@name='collationQuery']='lowerfilt:(paintablepine ample goodness)'",
+        "//lst[@name='collation'][3 ]/str[@name='collationQuery']='lowerfilt:(printable pineapple goodness)'",
+        "//lst[@name='collation'][4 ]/str[@name='collationQuery']='lowerfilt:((paint able) line ample goodness)'",
+        "//lst[@name='collation'][5 ]/str[@name='collationQuery']='lowerfilt:(printable (pi ne) ample goodness)'",
+        "//lst[@name='collation'][6 ]/str[@name='collationQuery']='lowerfilt:((paint able) pineapple goodness)'",
+        "//lst[@name='collation'][7 ]/str[@name='collationQuery']='lowerfilt:((paint able) (pi ne) ample goodness)'",
+        "//lst[@name='collation'][8 ]/str[@name='collationQuery']='lowerfilt:(pintable line ample goodness)'",
+        "//lst[@name='collation'][9 ]/str[@name='collationQuery']='lowerfilt:(pintable pineapple goodness)'",
+        "//lst[@name='collation'][10]/str[@name='collationQuery']='lowerfilt:(pintable (pi ne) ample goodness)'",
+        "//lst[@name='collation'][10]/lst[@name='misspellingsAndCorrections']/str[@name='paintable']='pintable'",
+        "//lst[@name='collation'][10]/lst[@name='misspellingsAndCorrections']/str[@name='pine']='pi ne'",
+        "//lst[@name='collation'][10]/lst[@name='misspellingsAndCorrections']/str[@name='apple']='ample'",
+        "//lst[@name='collation'][10]/lst[@name='misspellingsAndCorrections']/str[@name='godness']='goodness'"
+    );
+    assertQ(req(
+        "q", "lowerfilt:(pine AND apple)", 
+        "qt", "spellCheckWithWordbreak", 
+        "indent", "true",
+        SpellCheckComponent.COMPONENT_NAME, "true", 
+        SpellCheckComponent.SPELLCHECK_ACCURACY, ".75", 
+        SpellCheckComponent.SPELLCHECK_EXTENDED_RESULTS, "true",
+        SpellCheckComponent.SPELLCHECK_COLLATE, "true",
+        SpellCheckComponent.SPELLCHECK_COLLATE_EXTENDED_RESULTS, "true",
+        SpellCheckComponent.SPELLCHECK_MAX_COLLATIONS, "10"),
+        "//lst[@name='collation'][1 ]/str[@name='collationQuery']='lowerfilt:(line AND ample)'",
+        "//lst[@name='collation'][2 ]/str[@name='collationQuery']='lowerfilt:(pineapple)'",
+        "//lst[@name='collation'][3 ]/str[@name='collationQuery']='lowerfilt:((pi AND ne) AND ample)'"
+    );
+    assertQ(req(
+        "q", "lowerfilt:pine AND NOT lowerfilt:apple", 
+        "qt", "spellCheckWithWordbreak", 
+        "indent", "true",
+        SpellCheckComponent.COMPONENT_NAME, "true", 
+        SpellCheckComponent.SPELLCHECK_ACCURACY, ".75", 
+        SpellCheckComponent.SPELLCHECK_EXTENDED_RESULTS, "true",
+        SpellCheckComponent.SPELLCHECK_COLLATE, "true",
+        SpellCheckComponent.SPELLCHECK_COLLATE_EXTENDED_RESULTS, "true",
+        SpellCheckComponent.SPELLCHECK_MAX_COLLATIONS, "10"),
+        "//lst[@name='collation'][1 ]/str[@name='collationQuery']='lowerfilt:line AND NOT lowerfilt:ample'",
+        "//lst[@name='collation'][2 ]/str[@name='collationQuery']='lowerfilt:(pi AND ne) AND NOT lowerfilt:ample'"
+    );
+    assertQ(req(
+        "q", "lowerfilt:pine NOT lowerfilt:apple", 
+        "qt", "spellCheckWithWordbreak", 
+        "indent", "true",
+        SpellCheckComponent.COMPONENT_NAME, "true", 
+        SpellCheckComponent.SPELLCHECK_ACCURACY, ".75", 
+        SpellCheckComponent.SPELLCHECK_EXTENDED_RESULTS, "true",
+        SpellCheckComponent.SPELLCHECK_COLLATE, "true",
+        SpellCheckComponent.SPELLCHECK_COLLATE_EXTENDED_RESULTS, "true",
+        SpellCheckComponent.SPELLCHECK_MAX_COLLATIONS, "10"),
+        "//lst[@name='collation'][1 ]/str[@name='collationQuery']='lowerfilt:line NOT lowerfilt:ample'",
+        "//lst[@name='collation'][2 ]/str[@name='collationQuery']='lowerfilt:(pi AND ne) NOT lowerfilt:ample'"
+    );
+    assertQ(req(
+        "q", "lowerfilt:(+pine -apple)", 
+        "qt", "spellCheckWithWordbreak", 
+        "indent", "true",
+        SpellCheckComponent.COMPONENT_NAME, "true", 
+        SpellCheckComponent.SPELLCHECK_ACCURACY, ".75", 
+        SpellCheckComponent.SPELLCHECK_EXTENDED_RESULTS, "true",
+        SpellCheckComponent.SPELLCHECK_COLLATE, "true",
+        SpellCheckComponent.SPELLCHECK_COLLATE_EXTENDED_RESULTS, "true",
+        SpellCheckComponent.SPELLCHECK_MAX_COLLATIONS, "10"),
+        "//lst[@name='collation'][1 ]/str[@name='collationQuery']='lowerfilt:(+line -ample)'",
+        "//lst[@name='collation'][2 ]/str[@name='collationQuery']='lowerfilt:((+pi +ne) -ample)'"
+    );
+    assertQ(req(
+        "q", "lowerfilt:(+printableinpuntableplantable)", 
+        "qt", "spellCheckWithWordbreak", 
+        "indent", "true",
+        SpellCheckComponent.COMPONENT_NAME, "true", 
+        SpellCheckComponent.SPELLCHECK_ACCURACY, "1", 
+        SpellCheckComponent.SPELLCHECK_EXTENDED_RESULTS, "true",
+        SpellCheckComponent.SPELLCHECK_COLLATE, "true",
+        SpellCheckComponent.SPELLCHECK_COLLATE_EXTENDED_RESULTS, "true",
+        SpellCheckComponent.SPELLCHECK_MAX_COLLATIONS, "1"),
+        "//lst[@name='collation'][1 ]/str[@name='collationQuery']='lowerfilt:((+printable +in +puntable +plantable))'"
+    );    
+  }
+}

Modified: lucene/dev/trunk/solr/example/solr/conf/solrconfig.xml
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/example/solr/conf/solrconfig.xml?rev=1346058&r1=1346057&r2=1346058&view=diff
==============================================================================
--- lucene/dev/trunk/solr/example/solr/conf/solrconfig.xml (original)
+++ lucene/dev/trunk/solr/example/solr/conf/solrconfig.xml Mon Jun  4 17:55:25 2012
@@ -881,10 +881,14 @@
 
        <!-- Spell checking defaults -->
        <str name="spellcheck">on</str>
+       <str name="spellcheck.extendedResults">false</str>       
+       <str name="spellcheck.count">5</str>
+       <str name="spellcheck.alternativeTermCount">2</str>
+       <str name="spellcheck.maxResultsForSuggest">5</str>       
        <str name="spellcheck.collate">true</str>
-       <str name="spellcheck.onlyMorePopular">false</str>
-       <str name="spellcheck.extendedResults">false</str>
-       <str name="spellcheck.count">3</str>
+       <str name="spellcheck.collateExtendedResults">true</str>  
+       <str name="spellcheck.maxCollationTries">5</str>
+       <str name="spellcheck.maxCollations">3</str>           
      </lst>
 
      <!-- append spellchecking to our list of components -->
@@ -1171,6 +1175,16 @@
       	<float name="thresholdTokenFrequency">.01</float>
       -->
     </lst>
+    
+    <!-- a spellchecker that can break or combine words.  See "/spell" handler below for usage -->
+    <lst name="spellchecker">
+      <str name="name">wordbreak</str>
+      <str name="classname">solr.WordBreakSolrSpellChecker</str>      
+      <str name="field">name</str>
+      <str name="combineWords">true</str>
+      <str name="breakWords">true</str>
+      <int name="maxChanges">10</int>
+    </lst>
 
     <!-- a spellchecker that uses a different distance measure -->
     <!--
@@ -1226,9 +1240,21 @@
     -->
   <requestHandler name="/spell" class="solr.SearchHandler" startup="lazy">
     <lst name="defaults">
-      <str name="spellcheck.onlyMorePopular">false</str>
-      <str name="spellcheck.extendedResults">false</str>
-      <str name="spellcheck.count">1</str>
+      <!-- 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>