/* * 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. */ package org.apache.solr.spelling; import java.util.HashSet; import java.util.List; import java.util.Set; import org.apache.lucene.util.LuceneTestCase.Slow; import org.apache.lucene.util.LuceneTestCase.SuppressTempFileChecks; import org.apache.lucene.util.TestUtil; import org.apache.solr.SolrTestCaseJ4; import org.apache.solr.common.params.SolrParams; import org.apache.solr.common.params.CommonParams; import org.apache.solr.common.params.CursorMarkParams; import org.apache.solr.common.params.GroupParams; import org.apache.solr.common.params.ModifiableSolrParams; import org.apache.solr.common.params.SpellingParams; import org.apache.solr.common.util.NamedList; import org.apache.solr.common.util.SimpleOrderedMap; import org.apache.solr.core.SolrCore; import org.apache.solr.handler.component.SearchComponent; import org.apache.solr.handler.component.SpellCheckComponent; import org.apache.solr.request.LocalSolrQueryRequest; import org.apache.solr.request.SolrQueryRequest; import org.apache.solr.request.SolrRequestHandler; import org.apache.solr.response.SolrQueryResponse; import org.junit.BeforeClass; import org.junit.Test; @Slow @SuppressTempFileChecks(bugUrl = "https://issues.apache.org/jira/browse/SOLR-1877 Spellcheck IndexReader leak bug?") public class SpellCheckCollatorTest extends SolrTestCaseJ4 { //if adding documents to this test, adjust me. private static final int MAX_DOC_ID=16; private static final int MIN_DOC_ID_WITH_EVERYOTHER=0; private static final int MAX_DOC_ID_WITH_EVERYOTHER=15; @BeforeClass public static void beforeClass() throws Exception { initCore("solrconfig-spellcheckcomponent.xml", "schema.xml"); assertU(adoc("id", "0", "lowerfilt", "faith hope and love to", "teststop", "metanoia")); assertU(adoc("id", "1", "lowerfilt", "faith hope and loaves", "teststop", "everyother")); assertU(adoc("id", "2", "lowerfilt", "fat hops and loaves")); assertU(adoc("id", "3", "lowerfilt", "faith of homer", "teststop", "metanoia", "teststop", "everyother")); assertU(adoc("id", "4", "lowerfilt", "fat of homer")); assertU(adoc("id", "5", "lowerfilt1", "peace", "teststop", "everyother")); assertU(adoc("id", "6", "lowerfilt", "hyphenated word")); assertU(adoc("id", "7", "teststop", "Jane filled out a form at Charles De Gaulle", "teststop", "everyother")); assertU(adoc("id", "8", "teststop", "Dick flew from Heathrow")); assertU(adoc("id", "9", "teststop", "Jane is stuck in customs because Spot chewed up the form", "teststop", "everyother")); assertU(adoc("id", "10", "teststop", "Once in Paris Dick built a fire on the hearth")); assertU(adoc("id", "11", "teststop", "Dick waited for Jane as he watched the sparks flow upward", "teststop", "everyother")); assertU(adoc("id", "12", "teststop", "This June parisian rendez-vous is ruined because of a customs snafu")); assertU(adoc("id", "13", "teststop", "partisan political machine", "teststop", "metanoia", "teststop", "everyother")); assertU(adoc("id", "14", "teststop", "metanoia")); assertU(adoc("id", "15", "teststop", "metanoia", "teststop", "everyother")); assertU(adoc("id", "16", "teststop", "metanoia")); assertU(commit()); } @Test public void testCollationWithRangeQuery() throws Exception { SolrCore core = h.getCore(); SearchComponent speller = core.getSearchComponent("spellcheck"); assertTrue("speller is null and it shouldn't be", speller != null); ModifiableSolrParams params = new ModifiableSolrParams(); params.add(SpellCheckComponent.COMPONENT_NAME, "true"); params.add(SpellingParams.SPELLCHECK_BUILD, "true"); params.add(SpellingParams.SPELLCHECK_COUNT, "10"); params.add(SpellingParams.SPELLCHECK_COLLATE, "true"); params.add(SpellingParams.SPELLCHECK_ALTERNATIVE_TERM_COUNT, "10"); params.add(CommonParams.Q, "id:[1 TO 10] AND lowerfilt:lovw"); { SolrRequestHandler handler = core.getRequestHandler("spellCheckCompRH"); SolrQueryResponse rsp = new SolrQueryResponse(); rsp.addResponseHeader(new SimpleOrderedMap()); SolrQueryRequest req = new LocalSolrQueryRequest(core, params); handler.handleRequest(req, rsp); req.close(); NamedList values = rsp.getValues(); NamedList spellCheck = (NamedList) values.get("spellcheck"); NamedList collationHolder = (NamedList) spellCheck.get("collations"); List<String> collations = collationHolder.getAll("collation"); assertTrue(collations.size()==1); String collation = collations.iterator().next(); System.out.println(collation); assertTrue("Incorrect collation: " + collation,"id:[1 TO 10] AND lowerfilt:love".equals(collation)); } } @Test public void testCollationWithHypens() throws Exception { SolrCore core = h.getCore(); SearchComponent speller = core.getSearchComponent("spellcheck"); assertTrue("speller is null and it shouldn't be", speller != null); ModifiableSolrParams params = new ModifiableSolrParams(); params.add(SpellCheckComponent.COMPONENT_NAME, "true"); params.add(SpellingParams.SPELLCHECK_BUILD, "true"); params.add(SpellingParams.SPELLCHECK_COUNT, "10"); params.add(SpellingParams.SPELLCHECK_COLLATE, "true"); params.add(CommonParams.Q, "lowerfilt:(hypenated-wotd)"); { SolrRequestHandler handler = core.getRequestHandler("spellCheckCompRH"); SolrQueryResponse rsp = new SolrQueryResponse(); rsp.addResponseHeader(new SimpleOrderedMap()); SolrQueryRequest req = new LocalSolrQueryRequest(core, params); handler.handleRequest(req, rsp); req.close(); NamedList values = rsp.getValues(); NamedList spellCheck = (NamedList) values.get("spellcheck"); NamedList collationHolder = (NamedList) spellCheck.get("collations"); List<String> collations = collationHolder.getAll("collation"); assertTrue(collations.size()==1); String collation = collations.iterator().next(); assertTrue("Incorrect collation: " + collation,"lowerfilt:(hyphenated-word)".equals(collation)); } params.remove(CommonParams.Q); params.add("defType", "dismax"); params.add("qf", "lowerfilt"); params.add(CommonParams.Q, "hypenated-wotd"); { SolrRequestHandler handler = core.getRequestHandler("spellCheckCompRH"); SolrQueryResponse rsp = new SolrQueryResponse(); rsp.add("responseHeader", new SimpleOrderedMap()); SolrQueryRequest req = new LocalSolrQueryRequest(core, params); handler.handleRequest(req, rsp); req.close(); NamedList values = rsp.getValues(); NamedList spellCheck = (NamedList) values.get("spellcheck"); NamedList collationHolder = (NamedList) spellCheck.get("collations"); List<String> collations = collationHolder.getAll("collation"); assertTrue(collations.size()==1); String collation = collations.iterator().next(); assertTrue("Incorrect collation: " + collation,"hyphenated-word".equals(collation)); } } public void testCollateWithOverride() throws Exception { assertQ( req( SpellCheckComponent.COMPONENT_NAME, "true", SpellCheckComponent.SPELLCHECK_DICT, "direct", SpellingParams.SPELLCHECK_COUNT, "10", SpellingParams.SPELLCHECK_COLLATE, "true", SpellingParams.SPELLCHECK_MAX_COLLATION_TRIES, "10", SpellingParams.SPELLCHECK_MAX_COLLATIONS, "10", "qt", "spellCheckCompRH", "defType", "edismax", "qf", "teststop", "mm", "1", CommonParams.Q, "partisian politcal mashine" ), "//lst[@name='spellcheck']/lst[@name='collations']/str[@name='collation']='parisian political machine'" ); assertQ( req( SpellCheckComponent.COMPONENT_NAME, "true", SpellCheckComponent.SPELLCHECK_DICT, "direct", SpellingParams.SPELLCHECK_COUNT, "10", SpellingParams.SPELLCHECK_COLLATE, "true", SpellingParams.SPELLCHECK_MAX_COLLATION_TRIES, "10", SpellingParams.SPELLCHECK_MAX_COLLATIONS, "10", "qt", "spellCheckCompRH", "defType", "edismax", "qf", "teststop", "mm", "1", SpellingParams.SPELLCHECK_COLLATE_PARAM_OVERRIDE + "mm", "100%", CommonParams.Q, "partisian politcal mashine" ), "//lst[@name='spellcheck']/lst[@name='collations']/str[@name='collation']='partisan political machine'" ); } @Test public void testCollateWithFilter() throws Exception { SolrCore core = h.getCore(); SearchComponent speller = core.getSearchComponent("spellcheck"); assertTrue("speller is null and it shouldn't be", speller != null); ModifiableSolrParams params = new ModifiableSolrParams(); params.add(SpellCheckComponent.COMPONENT_NAME, "true"); params.add(SpellingParams.SPELLCHECK_BUILD, "true"); params.add(SpellingParams.SPELLCHECK_COUNT, "10"); params.add(SpellingParams.SPELLCHECK_COLLATE, "true"); params.add(SpellingParams.SPELLCHECK_MAX_COLLATION_TRIES, "10"); params.add(SpellingParams.SPELLCHECK_MAX_COLLATIONS, "10"); params.add(CommonParams.Q, "lowerfilt:(+fauth +home +loane)"); params.add(CommonParams.FQ, "NOT(id:1)"); //Because a FilterQuery is applied which removes doc id#1 from possible hits, we would //not want the collations to return us "lowerfilt:(+faith +hope +loaves)" as this only matches doc id#1. SolrRequestHandler handler = core.getRequestHandler("spellCheckCompRH"); SolrQueryResponse rsp = new SolrQueryResponse(); rsp.addResponseHeader(new SimpleOrderedMap()); SolrQueryRequest req = new LocalSolrQueryRequest(core, params); handler.handleRequest(req, rsp); req.close(); NamedList values = rsp.getValues(); NamedList spellCheck = (NamedList) values.get("spellcheck"); NamedList collationHolder = (NamedList) spellCheck.get("collations"); List<String> collations = collationHolder.getAll("collation"); assertTrue(collations.size() > 0); for(String collation : collations) { assertTrue(!collation.equals("lowerfilt:(+faith +hope +loaves)")); } } @Test public void testCollateWithMultipleRequestHandlers() throws Exception { SolrCore core = h.getCore(); SearchComponent speller = core.getSearchComponent("spellcheck"); assertTrue("speller is null and it shouldn't be", speller != null); ModifiableSolrParams params = new ModifiableSolrParams(); params.add(SpellCheckComponent.COMPONENT_NAME, "true"); params.add(SpellingParams.SPELLCHECK_DICT, "multipleFields"); params.add(SpellingParams.SPELLCHECK_BUILD, "true"); params.add(SpellingParams.SPELLCHECK_COUNT, "10"); params.add(SpellingParams.SPELLCHECK_COLLATE, "true"); params.add(SpellingParams.SPELLCHECK_MAX_COLLATION_TRIES, "1"); params.add(SpellingParams.SPELLCHECK_MAX_COLLATIONS, "1"); params.add(CommonParams.Q, "peac"); //SpellCheckCompRH has no "qf" defined. It will not find "peace" from "peac" despite it being in the dictionary //because requrying against this Request Handler results in 0 hits. SolrRequestHandler handler = core.getRequestHandler("spellCheckCompRH"); SolrQueryResponse rsp = new SolrQueryResponse(); rsp.addResponseHeader(new SimpleOrderedMap()); SolrQueryRequest req = new LocalSolrQueryRequest(core, params); handler.handleRequest(req, rsp); req.close(); NamedList values = rsp.getValues(); NamedList spellCheck = (NamedList) values.get("spellcheck"); NamedList collationHolder = (NamedList) spellCheck.get("collations"); String singleCollation = (String) collationHolder.get("collation"); assertNull(singleCollation); //SpellCheckCompRH1 has "lowerfilt1" defined in the "qf" param. It will find "peace" from "peac" because //requrying field "lowerfilt1" returns the hit. params.remove(SpellingParams.SPELLCHECK_BUILD); handler = core.getRequestHandler("spellCheckCompRH1"); rsp = new SolrQueryResponse(); rsp.addResponseHeader(new SimpleOrderedMap()); req = new LocalSolrQueryRequest(core, params); handler.handleRequest(req, rsp); req.close(); values = rsp.getValues(); spellCheck = (NamedList) values.get("spellcheck"); collationHolder = (NamedList) spellCheck.get("collations"); singleCollation = (String) collationHolder.get("collation"); assertEquals(singleCollation, "peace"); } @Test public void testExtendedCollate() throws Exception { SolrCore core = h.getCore(); SearchComponent speller = core.getSearchComponent("spellcheck"); assertTrue("speller is null and it shouldn't be", speller != null); ModifiableSolrParams params = new ModifiableSolrParams(); params.add(CommonParams.QT, "spellCheckCompRH"); params.add(CommonParams.Q, "lowerfilt:(+fauth +home +loane)"); params.add(SpellingParams.SPELLCHECK_EXTENDED_RESULTS, "true"); params.add(SpellCheckComponent.COMPONENT_NAME, "true"); params.add(SpellingParams.SPELLCHECK_BUILD, "true"); params.add(SpellingParams.SPELLCHECK_COUNT, "10"); params.add(SpellingParams.SPELLCHECK_COLLATE, "true"); // Testing backwards-compatible behavior. // Returns 1 collation as a single string. // All words are "correct" per the dictionary, but this collation would // return no results if tried. SolrRequestHandler handler = core.getRequestHandler("spellCheckCompRH"); SolrQueryResponse rsp = new SolrQueryResponse(); rsp.addResponseHeader(new SimpleOrderedMap()); SolrQueryRequest req = new LocalSolrQueryRequest(core, params); handler.handleRequest(req, rsp); req.close(); NamedList values = rsp.getValues(); NamedList spellCheck = (NamedList) values.get("spellcheck"); NamedList collationHolder = (NamedList) spellCheck.get("collations"); String singleCollation = (String) collationHolder.get("collation"); assertEquals("lowerfilt:(+faith +homer +loaves)", singleCollation); // Testing backwards-compatible response format but will only return a // collation that would return results. params.remove(SpellingParams.SPELLCHECK_BUILD); params.add(SpellingParams.SPELLCHECK_MAX_COLLATION_TRIES, "5"); params.add(SpellingParams.SPELLCHECK_MAX_COLLATIONS, "1"); handler = core.getRequestHandler("spellCheckCompRH"); rsp = new SolrQueryResponse(); rsp.addResponseHeader(new SimpleOrderedMap()); req = new LocalSolrQueryRequest(core, params); handler.handleRequest(req, rsp); req.close(); values = rsp.getValues(); spellCheck = (NamedList) values.get("spellcheck"); collationHolder = (NamedList) spellCheck.get("collations"); singleCollation = (String) collationHolder.get("collation"); assertEquals("lowerfilt:(+faith +hope +loaves)", singleCollation); // Testing returning multiple collations if more than one valid // combination exists. params.remove(SpellingParams.SPELLCHECK_MAX_COLLATION_TRIES); params.remove(SpellingParams.SPELLCHECK_MAX_COLLATIONS); params.add(SpellingParams.SPELLCHECK_MAX_COLLATION_TRIES, "10"); params.add(SpellingParams.SPELLCHECK_MAX_COLLATIONS, "2"); handler = core.getRequestHandler("spellCheckCompRH"); rsp = new SolrQueryResponse(); rsp.addResponseHeader(new SimpleOrderedMap()); req = new LocalSolrQueryRequest(core, params); handler.handleRequest(req, rsp); req.close(); values = rsp.getValues(); spellCheck = (NamedList) values.get("spellcheck"); collationHolder = (NamedList) spellCheck.get("collations"); List<String> collations = collationHolder.getAll("collation"); assertTrue(collations.size() == 2); for (String multipleCollation : collations) { assertTrue(multipleCollation.equals("lowerfilt:(+faith +hope +love)") || multipleCollation.equals("lowerfilt:(+faith +hope +loaves)")); } // Testing return multiple collations with expanded collation response // format. params.add(SpellingParams.SPELLCHECK_COLLATE_EXTENDED_RESULTS, "true"); handler = core.getRequestHandler("spellCheckCompRH"); rsp = new SolrQueryResponse(); rsp.addResponseHeader(new SimpleOrderedMap()); req = new LocalSolrQueryRequest(core, params); handler.handleRequest(req, rsp); req.close(); values = rsp.getValues(); spellCheck = (NamedList) values.get("spellcheck"); collationHolder = (NamedList) spellCheck.get("collations"); List<NamedList> expandedCollationList = collationHolder.getAll("collation"); Set<String> usedcollations = new HashSet<>(); assertTrue(expandedCollationList.size() == 2); for (NamedList expandedCollation : expandedCollationList) { String multipleCollation = (String) expandedCollation.get("collationQuery"); assertTrue(multipleCollation.equals("lowerfilt:(+faith +hope +love)") || multipleCollation.equals("lowerfilt:(+faith +hope +loaves)")); assertTrue(!usedcollations.contains(multipleCollation)); usedcollations.add(multipleCollation); int hits = (Integer) expandedCollation.get("hits"); assertTrue(hits == 1); NamedList misspellingsAndCorrections = (NamedList) expandedCollation.get("misspellingsAndCorrections"); assertTrue(misspellingsAndCorrections.size() == 3); String correctionForFauth = (String) misspellingsAndCorrections.get("fauth"); String correctionForHome = (String) misspellingsAndCorrections.get("home"); String correctionForLoane = (String) misspellingsAndCorrections.get("loane"); assertTrue(correctionForFauth.equals("faith")); assertTrue(correctionForHome.equals("hope")); assertTrue(correctionForLoane.equals("love") || correctionForLoane.equals("loaves")); } } @Test public void testCollateWithGrouping() throws Exception { SolrCore core = h.getCore(); SearchComponent speller = core.getSearchComponent("spellcheck"); assertTrue("speller is null and it shouldn't be", speller != null); ModifiableSolrParams params = new ModifiableSolrParams(); params.add(SpellCheckComponent.COMPONENT_NAME, "true"); params.add(SpellingParams.SPELLCHECK_BUILD, "true"); params.add(SpellingParams.SPELLCHECK_COUNT, "10"); params.add(SpellingParams.SPELLCHECK_COLLATE, "true"); params.add(SpellingParams.SPELLCHECK_MAX_COLLATION_TRIES, "5"); params.add(SpellingParams.SPELLCHECK_MAX_COLLATIONS, "1"); params.add(CommonParams.Q, "lowerfilt:(+fauth)"); params.add(GroupParams.GROUP, "true"); params.add(GroupParams.GROUP_FIELD, "id"); //Because a FilterQuery is applied which removes doc id#1 from possible hits, we would //not want the collations to return us "lowerfilt:(+faith +hope +loaves)" as this only matches doc id#1. SolrRequestHandler handler = core.getRequestHandler("spellCheckCompRH"); SolrQueryResponse rsp = new SolrQueryResponse(); rsp.addResponseHeader(new SimpleOrderedMap()); SolrQueryRequest req = new LocalSolrQueryRequest(core, params); handler.handleRequest(req, rsp); req.close(); NamedList values = rsp.getValues(); NamedList spellCheck = (NamedList) values.get("spellcheck"); NamedList collationHolder = (NamedList) spellCheck.get("collations"); List<String> collations = collationHolder.getAll("collation"); assertTrue(collations.size() == 1); } @Test public void testContextSensitiveCollate() throws Exception { // DirectSolrSpellChecker IndexBasedSpellChecker String[] dictionary = {"direct", "default_teststop" }; for(int i=0 ; i<=1 ; i++) { assertQ( req( "q", "teststop:(flew AND form AND heathrow)", "qt", "spellCheckCompRH", "indent", "true", SpellCheckComponent.COMPONENT_NAME, "true", SpellCheckComponent.SPELLCHECK_BUILD, "true", SpellCheckComponent.SPELLCHECK_DICT, dictionary[i], SpellCheckComponent.SPELLCHECK_EXTENDED_RESULTS, "true", SpellCheckComponent.SPELLCHECK_COUNT, "10", SpellCheckComponent.SPELLCHECK_ALTERNATIVE_TERM_COUNT, "5", SpellCheckComponent.SPELLCHECK_MAX_RESULTS_FOR_SUGGEST, "0", SpellCheckComponent.SPELLCHECK_COLLATE, "true", SpellCheckComponent.SPELLCHECK_MAX_COLLATION_TRIES, "10", SpellCheckComponent.SPELLCHECK_MAX_COLLATIONS, "1", SpellCheckComponent.SPELLCHECK_COLLATE_EXTENDED_RESULTS, "true" ), "//lst[@name='spellcheck']/lst[@name='suggestions']/lst[@name='flew']/arr[@name='suggestion']/lst/str[@name='word']='flow'", "//lst[@name='spellcheck']/lst[@name='suggestions']/lst[@name='form']/arr[@name='suggestion']/lst/str[@name='word']='from'", /* DirectSolrSpellChecker won't suggest if the edit distance > 2, so we can't test for this one... "//lst[@name='spellcheck']/lst[@name='suggestions']/lst[@name='heathrow']/arr[@name='suggestion']/lst/str[@name='word']='hearth'", */ "//lst[@name='spellcheck']/bool[@name='correctlySpelled']='false'", "//lst[@name='spellcheck']/lst[@name='collations']/lst[@name='collation']/str[@name='collationQuery']='teststop:(flew AND from AND heathrow)'", "//lst[@name='spellcheck']/lst[@name='collations']/lst[@name='collation']/int[@name='hits']=1", "//lst[@name='spellcheck']/lst[@name='collations']/lst[@name='collation']/lst[@name='misspellingsAndCorrections']/str[@name='form']='from'" ); assertQ( req( "q", "teststop:(june AND customs)", "qt", "spellCheckCompRH", "indent", "true", SpellCheckComponent.COMPONENT_NAME, "true", SpellCheckComponent.SPELLCHECK_DICT, dictionary[i], SpellCheckComponent.SPELLCHECK_EXTENDED_RESULTS, "true", SpellCheckComponent.SPELLCHECK_COUNT, "10", SpellCheckComponent.SPELLCHECK_ALTERNATIVE_TERM_COUNT, "5", SpellCheckComponent.SPELLCHECK_MAX_RESULTS_FOR_SUGGEST, "1", SpellCheckComponent.SPELLCHECK_COLLATE, "true", SpellCheckComponent.SPELLCHECK_MAX_COLLATION_TRIES, "10", SpellCheckComponent.SPELLCHECK_MAX_COLLATIONS, "1", SpellCheckComponent.SPELLCHECK_COLLATE_EXTENDED_RESULTS, "true" ), "//result[@numFound=1]", "//lst[@name='spellcheck']/lst[@name='suggestions']/lst[@name='june']/arr[@name='suggestion']/lst/str[@name='word']='jane'", "//lst[@name='spellcheck']/bool[@name='correctlySpelled']='false'", "//lst[@name='spellcheck']/lst[@name='collations']/lst[@name='collation']/str[@name='collationQuery']='teststop:(jane AND customs)'", "//lst[@name='spellcheck']/lst[@name='collations']/lst[@name='collation']/int[@name='hits']=1", "//lst[@name='spellcheck']/lst[@name='collations']/lst[@name='collation']/lst[@name='misspellingsAndCorrections']/str[@name='june']='jane'" ); //SOLR-5090, alternativeTermCount==0 was being evaluated, sometimes would throw NPE assertQ(req("q", "teststop:(june customs)", "mm", "2", "qt", "spellCheckCompRH", "indent", "true", SpellCheckComponent.COMPONENT_NAME, "true", SpellCheckComponent.SPELLCHECK_DICT, dictionary[i], SpellCheckComponent.SPELLCHECK_COUNT, "10", SpellCheckComponent.SPELLCHECK_ALTERNATIVE_TERM_COUNT, "0", SpellCheckComponent.SPELLCHECK_COLLATE, "true")); } } @Test public void testEstimatedHitCounts() throws Exception { final String xpathPrefix = "//lst[@name='spellcheck']/lst[@name='collations']/lst[@name='collation']/"; final SolrParams reusedParams = params (SpellCheckComponent.COMPONENT_NAME, "true", SpellCheckComponent.SPELLCHECK_DICT, "direct", SpellingParams.SPELLCHECK_COUNT, "1", SpellingParams.SPELLCHECK_COLLATE, "true", SpellingParams.SPELLCHECK_MAX_COLLATION_TRIES, "1", SpellingParams.SPELLCHECK_MAX_COLLATIONS, "1", SpellingParams.SPELLCHECK_COLLATE_EXTENDED_RESULTS, "true", "qt", "spellCheckCompRH"); // default case, no SPELLCHECK_COLLATE_MAX_COLLECT_DOCS should be exact num hits assertQ(req(reusedParams, CommonParams.Q, "teststop:metnoia") , xpathPrefix + "str[@name='collationQuery']='teststop:metanoia'" , xpathPrefix + "int[@name='hits']=6" ); // specifying 0 means "exact" same as default, but specifing a value greater // then the total number of docs in the index should also result in it // "estimating" and getting exact number as well. for (String val : new String[] { "0", "30", "100", "10000" }) { assertQ(req(reusedParams, CommonParams.Q, "teststop:metnoia", SpellingParams.SPELLCHECK_COLLATE_MAX_COLLECT_DOCS, val) , xpathPrefix + "str[@name='collationQuery']='teststop:metanoia'" , xpathPrefix + "int[@name='hits']=6" ); } // values between 0 and the num docs in the index should not error, and should // produce an estimate no more then the total number of docs final int iters = atLeast(10); for (int iter = 0; iter < iters; iter++) { final int val = TestUtil.nextInt(random(), 1, 17); assertQ(req(reusedParams, CommonParams.Q, "teststop:metnoia", SpellingParams.SPELLCHECK_COLLATE_MAX_COLLECT_DOCS, ""+val) , xpathPrefix + "str[@name='collationQuery']='teststop:metanoia'" , xpathPrefix + "int[@name='hits' and . <= 17 and 0 < .]" ); } // "everYother" appears in every other doc in the index, so "everother" // should produce a "decent" aproximation of the number of hits (8) // for any 5 <= SPELLCHECK_COLLATE_MAX_COLLECT_DOCS // // (we have to be kind of flexible with our definition of "decent" // since we're dealing with a fairly small index here) for (int val = 5; val <= 20; val++) { int max = MAX_DOC_ID * val / (val + MIN_DOC_ID_WITH_EVERYOTHER + 1); int min = MAX_DOC_ID * val / (val + MAX_DOC_ID_WITH_EVERYOTHER + 1); assertQ(req(reusedParams, CommonParams.Q, "teststop:everother", SpellingParams.SPELLCHECK_COLLATE_MAX_COLLECT_DOCS, ""+val) , xpathPrefix + "str[@name='collationQuery']='teststop:everyother'" , xpathPrefix + "int[@name='hits' and " + min + " <= . and . <= " + max + "]" ); } } @Test public void testZeroTries() throws Exception { SolrCore core = h.getCore(); SearchComponent speller = core.getSearchComponent("spellcheck"); assertTrue("speller is null and it shouldn't be", speller != null); ModifiableSolrParams params = new ModifiableSolrParams(); params.add(SpellCheckComponent.COMPONENT_NAME, "true"); params.add(SpellCheckComponent.SPELLCHECK_BUILD, "true"); params.add(SpellCheckComponent.SPELLCHECK_COUNT, "10"); params.add(SpellCheckComponent.SPELLCHECK_COLLATE, "true"); params.add(SpellCheckComponent.SPELLCHECK_MAX_COLLATION_TRIES, "0"); params.add(SpellCheckComponent.SPELLCHECK_MAX_COLLATIONS, "2"); params.add(CommonParams.Q, "lowerfilt:(+fauth)"); SolrRequestHandler handler = core.getRequestHandler("spellCheckCompRH"); SolrQueryResponse rsp = new SolrQueryResponse(); rsp.addResponseHeader(new SimpleOrderedMap()); SolrQueryRequest req = new LocalSolrQueryRequest(core, params); handler.handleRequest(req, rsp); req.close(); NamedList values = rsp.getValues(); NamedList spellCheck = (NamedList) values.get("spellcheck"); NamedList collationList = (NamedList) spellCheck.get("collations"); List<?> collations = (List<?>) collationList.getAll("collation"); assertTrue(collations.size() == 2); } @Test public void testWithCursorMark() throws Exception { SolrCore core = h.getCore(); SearchComponent speller = core.getSearchComponent("spellcheck"); assertTrue("speller is null and it shouldn't be", speller != null); ModifiableSolrParams params = new ModifiableSolrParams(); params.add(SpellCheckComponent.COMPONENT_NAME, "true"); params.add(SpellCheckComponent.SPELLCHECK_BUILD, "true"); params.add(SpellCheckComponent.SPELLCHECK_COUNT, "10"); params.add(SpellCheckComponent.SPELLCHECK_COLLATE, "true"); params.add(SpellCheckComponent.SPELLCHECK_MAX_COLLATION_TRIES, "2"); params.add(SpellCheckComponent.SPELLCHECK_MAX_COLLATIONS, "1"); params.add(CommonParams.Q, "lowerfilt:(+fauth)"); params.add(CommonParams.SORT, "id asc"); params.add(CursorMarkParams.CURSOR_MARK_PARAM, CursorMarkParams.CURSOR_MARK_START); SolrRequestHandler handler = core.getRequestHandler("spellCheckCompRH"); SolrQueryResponse rsp = new SolrQueryResponse(); rsp.addResponseHeader(new SimpleOrderedMap()); SolrQueryRequest req = new LocalSolrQueryRequest(core, params); handler.handleRequest(req, rsp); req.close(); NamedList values = rsp.getValues(); NamedList spellCheck = (NamedList) values.get("spellcheck"); NamedList collationList = (NamedList) spellCheck.get("collations"); List<?> collations = (List<?>) collationList.getAll("collation"); assertTrue(collations.size() == 1); } }