mirror of https://github.com/apache/lucene.git
SOLR-7888: Analyzing suggesters can now filter suggestions by a context field
git-svn-id: https://svn.apache.org/repos/asf/lucene/dev/trunk@1707907 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
bbcea96908
commit
b831a14ba5
|
@ -24,6 +24,7 @@ import java.util.Comparator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.apache.lucene.search.BooleanQuery;
|
||||||
import org.apache.lucene.search.spell.Dictionary;
|
import org.apache.lucene.search.spell.Dictionary;
|
||||||
import org.apache.lucene.store.DataInput;
|
import org.apache.lucene.store.DataInput;
|
||||||
import org.apache.lucene.store.DataOutput;
|
import org.apache.lucene.store.DataOutput;
|
||||||
|
@ -251,6 +252,22 @@ public abstract class Lookup implements Accountable {
|
||||||
*/
|
*/
|
||||||
public abstract List<LookupResult> lookup(CharSequence key, Set<BytesRef> contexts, boolean onlyMorePopular, int num) throws IOException;
|
public abstract List<LookupResult> lookup(CharSequence key, Set<BytesRef> contexts, boolean onlyMorePopular, int num) throws IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Look up a key and return possible completion for this key.
|
||||||
|
* This needs to be overridden by all implementing classes as the default implementation just returns null
|
||||||
|
*
|
||||||
|
* @param key the lookup key
|
||||||
|
* @param contextFilerQuery A query for further filtering the result of the key lookup
|
||||||
|
* @param num maximum number of results to return
|
||||||
|
* @param allTermsRequired true is all terms are required
|
||||||
|
* @param doHighlight set to true if key should be highlighted
|
||||||
|
* @return a list of suggestions/completions. The default implementation returns null, meaning each @Lookup implementation should override this and provide their own implementation
|
||||||
|
* @throws IOException when IO exception occurs
|
||||||
|
*/
|
||||||
|
public List<LookupResult> lookup(CharSequence key, BooleanQuery contextFilerQuery, int num, boolean allTermsRequired, boolean doHighlight) throws IOException{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Persist the constructed lookup data to a directory. Optional operation.
|
* Persist the constructed lookup data to a directory. Optional operation.
|
||||||
* @param output {@link DataOutput} to write the data to.
|
* @param output {@link DataOutput} to write the data to.
|
||||||
|
|
|
@ -176,6 +176,8 @@ New Features
|
||||||
|
|
||||||
* SOLR-7858: Add links between original and new Admin UIs (Upayavira)
|
* SOLR-7858: Add links between original and new Admin UIs (Upayavira)
|
||||||
|
|
||||||
|
* SOLR-7888: Analyzing suggesters can now filter suggestions by a context field (Arcadius Ahouansou, janhoy)
|
||||||
|
|
||||||
Bug Fixes
|
Bug Fixes
|
||||||
----------------------
|
----------------------
|
||||||
|
|
||||||
|
|
|
@ -186,7 +186,7 @@ public class SuggestComponent extends SearchComponent implements SolrCoreAware,
|
||||||
rb.rsp.add("command", (!reloadAll) ? "reload" : "reloadAll");
|
rb.rsp.add("command", (!reloadAll) ? "reload" : "reloadAll");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Dispatch shard request in <code>STAGE_EXECUTE_QUERY</code> stage */
|
/** Dispatch shard request in <code>STAGE_EXECUTE_QUERY</code> stage */
|
||||||
@Override
|
@Override
|
||||||
public int distributedProcess(ResponseBuilder rb) {
|
public int distributedProcess(ResponseBuilder rb) {
|
||||||
|
@ -238,11 +238,21 @@ public class SuggestComponent extends SearchComponent implements SolrCoreAware,
|
||||||
query = params.get(CommonParams.Q);
|
query = params.get(CommonParams.Q);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (query != null) {
|
if (query != null) {
|
||||||
int count = params.getInt(SUGGEST_COUNT, 1);
|
int count = params.getInt(SUGGEST_COUNT, 1);
|
||||||
SuggesterOptions options = new SuggesterOptions(new CharsRef(query), count);
|
boolean highlight = params.getBool(SUGGEST_HIGHLIGHT, false);
|
||||||
Map<String, SimpleOrderedMap<NamedList<Object>>> namedListResults =
|
boolean allTermsRequired = params.getBool(SUGGEST_ALL_TERMS_REQUIRED, true);
|
||||||
|
String contextFilter = params.get(SUGGEST_CONTEXT_FILTER_QUERY);
|
||||||
|
if (contextFilter != null) {
|
||||||
|
contextFilter = contextFilter.trim();
|
||||||
|
if (contextFilter.length() == 0) {
|
||||||
|
contextFilter = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SuggesterOptions options = new SuggesterOptions(new CharsRef(query), count, contextFilter, allTermsRequired, highlight);
|
||||||
|
Map<String, SimpleOrderedMap<NamedList<Object>>> namedListResults =
|
||||||
new HashMap<>();
|
new HashMap<>();
|
||||||
for (SolrSuggester suggester : querySuggesters) {
|
for (SolrSuggester suggester : querySuggesters) {
|
||||||
SuggesterResult suggesterResult = suggester.getSuggestions(options);
|
SuggesterResult suggesterResult = suggester.getSuggestions(options);
|
||||||
|
@ -251,7 +261,7 @@ public class SuggestComponent extends SearchComponent implements SolrCoreAware,
|
||||||
rb.rsp.add(SuggesterResultLabels.SUGGEST, namedListResults);
|
rb.rsp.add(SuggesterResultLabels.SUGGEST, namedListResults);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used in Distributed Search, merges the suggestion results from every shard
|
* Used in Distributed Search, merges the suggestion results from every shard
|
||||||
* */
|
* */
|
||||||
|
|
|
@ -33,6 +33,8 @@ public class DocumentDictionaryFactory extends DictionaryFactory {
|
||||||
|
|
||||||
public static final String PAYLOAD_FIELD = "payloadField";
|
public static final String PAYLOAD_FIELD = "payloadField";
|
||||||
|
|
||||||
|
public static final String CONTEXT_FIELD = "contextField";
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Dictionary create(SolrCore core, SolrIndexSearcher searcher) {
|
public Dictionary create(SolrCore core, SolrIndexSearcher searcher) {
|
||||||
if(params == null) {
|
if(params == null) {
|
||||||
|
@ -42,12 +44,13 @@ public class DocumentDictionaryFactory extends DictionaryFactory {
|
||||||
String field = (String) params.get(FIELD);
|
String field = (String) params.get(FIELD);
|
||||||
String weightField = (String) params.get(WEIGHT_FIELD);
|
String weightField = (String) params.get(WEIGHT_FIELD);
|
||||||
String payloadField = (String) params.get(PAYLOAD_FIELD);
|
String payloadField = (String) params.get(PAYLOAD_FIELD);
|
||||||
|
String contextField = (String) params.get(CONTEXT_FIELD);
|
||||||
|
|
||||||
if (field == null) {
|
if (field == null) {
|
||||||
throw new IllegalArgumentException(FIELD + " is a mandatory parameter");
|
throw new IllegalArgumentException(FIELD + " is a mandatory parameter");
|
||||||
}
|
}
|
||||||
|
|
||||||
return new DocumentDictionary(searcher.getIndexReader(), field, weightField, payloadField);
|
return new DocumentDictionary(searcher.getIndexReader(), field, weightField, payloadField, contextField);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,13 +23,22 @@ import java.io.FileInputStream;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.apache.lucene.analysis.Analyzer;
|
||||||
|
import org.apache.lucene.analysis.standard.StandardTokenizerFactory;
|
||||||
|
import org.apache.lucene.queryparser.flexible.core.QueryNodeException;
|
||||||
|
import org.apache.lucene.queryparser.flexible.standard.StandardQueryParser;
|
||||||
|
import org.apache.lucene.search.BooleanClause;
|
||||||
|
import org.apache.lucene.search.BooleanQuery;
|
||||||
|
import org.apache.lucene.search.Query;
|
||||||
import org.apache.lucene.search.spell.Dictionary;
|
import org.apache.lucene.search.spell.Dictionary;
|
||||||
import org.apache.lucene.search.suggest.Lookup;
|
import org.apache.lucene.search.suggest.Lookup;
|
||||||
import org.apache.lucene.search.suggest.Lookup.LookupResult;
|
import org.apache.lucene.search.suggest.Lookup.LookupResult;
|
||||||
import org.apache.lucene.util.Accountable;
|
import org.apache.lucene.util.Accountable;
|
||||||
import org.apache.lucene.util.IOUtils;
|
import org.apache.lucene.util.IOUtils;
|
||||||
|
import org.apache.solr.analysis.TokenizerChain;
|
||||||
import org.apache.solr.common.util.NamedList;
|
import org.apache.solr.common.util.NamedList;
|
||||||
import org.apache.solr.core.CloseHook;
|
import org.apache.solr.core.CloseHook;
|
||||||
import org.apache.solr.core.SolrCore;
|
import org.apache.solr.core.SolrCore;
|
||||||
|
@ -38,6 +47,7 @@ import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import static org.apache.solr.common.params.CommonParams.NAME;
|
import static org.apache.solr.common.params.CommonParams.NAME;
|
||||||
|
import static org.apache.solr.spelling.suggest.fst.AnalyzingInfixLookupFactory.CONTEXTS_FIELD_NAME;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Responsible for loading the lookup and dictionary Implementations specified by
|
* Responsible for loading the lookup and dictionary Implementations specified by
|
||||||
|
@ -61,7 +71,7 @@ public class SolrSuggester implements Accountable {
|
||||||
|
|
||||||
/** Fully-qualified class of the {@link Dictionary} implementation */
|
/** Fully-qualified class of the {@link Dictionary} implementation */
|
||||||
public static final String DICTIONARY_IMPL = "dictionaryImpl";
|
public static final String DICTIONARY_IMPL = "dictionaryImpl";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Name of the location where to persist the dictionary. If this location
|
* Name of the location where to persist the dictionary. If this location
|
||||||
* is relative then the data will be stored under the core's dataDir. If this
|
* is relative then the data will be stored under the core's dataDir. If this
|
||||||
|
@ -81,8 +91,9 @@ public class SolrSuggester implements Accountable {
|
||||||
|
|
||||||
private LookupFactory factory;
|
private LookupFactory factory;
|
||||||
private DictionaryFactory dictionaryFactory;
|
private DictionaryFactory dictionaryFactory;
|
||||||
|
private Analyzer contextFilterQueryAnalyzer;
|
||||||
/**
|
|
||||||
|
/**
|
||||||
* Uses the <code>config</code> and the <code>core</code> to initialize the underlying
|
* Uses the <code>config</code> and the <code>core</code> to initialize the underlying
|
||||||
* Lucene suggester
|
* Lucene suggester
|
||||||
* */
|
* */
|
||||||
|
@ -101,6 +112,9 @@ public class SolrSuggester implements Accountable {
|
||||||
lookupImpl = LookupFactory.DEFAULT_FILE_BASED_DICT;
|
lookupImpl = LookupFactory.DEFAULT_FILE_BASED_DICT;
|
||||||
LOG.info("No " + LOOKUP_IMPL + " parameter was provided falling back to " + lookupImpl);
|
LOG.info("No " + LOOKUP_IMPL + " parameter was provided falling back to " + lookupImpl);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
contextFilterQueryAnalyzer = new TokenizerChain(new StandardTokenizerFactory(Collections.EMPTY_MAP), null);
|
||||||
|
|
||||||
// initialize appropriate lookup instance
|
// initialize appropriate lookup instance
|
||||||
factory = core.getResourceLoader().newInstance(lookupImpl, LookupFactory.class);
|
factory = core.getResourceLoader().newInstance(lookupImpl, LookupFactory.class);
|
||||||
lookup = factory.create(config, core);
|
lookup = factory.create(config, core);
|
||||||
|
@ -146,7 +160,7 @@ public class SolrSuggester implements Accountable {
|
||||||
DictionaryFactory.DEFAULT_FILE_BASED_DICT;
|
DictionaryFactory.DEFAULT_FILE_BASED_DICT;
|
||||||
LOG.info("No " + DICTIONARY_IMPL + " parameter was provided falling back to " + dictionaryImpl);
|
LOG.info("No " + DICTIONARY_IMPL + " parameter was provided falling back to " + dictionaryImpl);
|
||||||
}
|
}
|
||||||
|
|
||||||
dictionaryFactory = core.getResourceLoader().newInstance(dictionaryImpl, DictionaryFactory.class);
|
dictionaryFactory = core.getResourceLoader().newInstance(dictionaryImpl, DictionaryFactory.class);
|
||||||
dictionaryFactory.setParams(config);
|
dictionaryFactory.setParams(config);
|
||||||
LOG.info("Dictionary loaded with params: " + config);
|
LOG.info("Dictionary loaded with params: " + config);
|
||||||
|
@ -212,11 +226,41 @@ public class SolrSuggester implements Accountable {
|
||||||
}
|
}
|
||||||
|
|
||||||
SuggesterResult res = new SuggesterResult();
|
SuggesterResult res = new SuggesterResult();
|
||||||
List<LookupResult> suggestions = lookup.lookup(options.token, false, options.count);
|
List<LookupResult> suggestions;
|
||||||
|
if(options.contextFilterQuery == null){
|
||||||
|
//TODO: this path needs to be fixed to accept query params to override configs such as allTermsRequired, highlight
|
||||||
|
suggestions = lookup.lookup(options.token, false, options.count);
|
||||||
|
} else {
|
||||||
|
BooleanQuery query = parseContextFilterQuery(options.contextFilterQuery);
|
||||||
|
suggestions = lookup.lookup(options.token, query, options.count, options.allTermsRequired, options.highlight);
|
||||||
|
if(suggestions == null){
|
||||||
|
// Context filtering not supported/configured by lookup
|
||||||
|
// Silently ignore filtering and serve a result by querying without context filtering
|
||||||
|
LOG.debug("Context Filtering Query not supported by {}", lookup.getClass());
|
||||||
|
suggestions = lookup.lookup(options.token, false, options.count);
|
||||||
|
}
|
||||||
|
}
|
||||||
res.add(getName(), options.token.toString(), suggestions);
|
res.add(getName(), options.token.toString(), suggestions);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private BooleanQuery parseContextFilterQuery(String contextFilter) {
|
||||||
|
if(contextFilter == null){
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Query query = null;
|
||||||
|
try {
|
||||||
|
query = new StandardQueryParser(contextFilterQueryAnalyzer).parse(contextFilter, CONTEXTS_FIELD_NAME);
|
||||||
|
if (query instanceof BooleanQuery) {
|
||||||
|
return (BooleanQuery) query;
|
||||||
|
}
|
||||||
|
return new BooleanQuery.Builder().add(query, BooleanClause.Occur.MUST).build();
|
||||||
|
} catch (QueryNodeException e) {
|
||||||
|
throw new IllegalArgumentException("Failed to parse query: " + query);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** Returns the unique name of the suggester */
|
/** Returns the unique name of the suggester */
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return name;
|
return name;
|
||||||
|
|
|
@ -30,9 +30,21 @@ public class SuggesterOptions {
|
||||||
|
|
||||||
/** Number of suggestions requested */
|
/** Number of suggestions requested */
|
||||||
int count;
|
int count;
|
||||||
|
|
||||||
public SuggesterOptions(CharsRef token, int count) {
|
/** A Solr or Lucene query for filtering suggestions*/
|
||||||
|
String contextFilterQuery;
|
||||||
|
|
||||||
|
/** Are all terms required?*/
|
||||||
|
boolean allTermsRequired;
|
||||||
|
|
||||||
|
/** Highlight term in results?*/
|
||||||
|
boolean highlight;
|
||||||
|
|
||||||
|
public SuggesterOptions(CharsRef token, int count, String contextFilterQuery, boolean allTermsRequired, boolean highlight) {
|
||||||
this.token = token;
|
this.token = token;
|
||||||
this.count = count;
|
this.count = count;
|
||||||
|
this.contextFilterQuery = contextFilterQuery;
|
||||||
|
this.allTermsRequired = allTermsRequired;
|
||||||
|
this.highlight = highlight;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -66,4 +66,21 @@ public interface SuggesterParams {
|
||||||
* This parameter does not need any suggest dictionary names to be specified
|
* This parameter does not need any suggest dictionary names to be specified
|
||||||
*/
|
*/
|
||||||
public static final String SUGGEST_RELOAD_ALL = SUGGEST_PREFIX + "reloadAll";
|
public static final String SUGGEST_RELOAD_ALL = SUGGEST_PREFIX + "reloadAll";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* contextFilterQuery to use for filtering the result of the suggestion
|
||||||
|
*/
|
||||||
|
public static final String SUGGEST_CONTEXT_FILTER_QUERY = SUGGEST_PREFIX + "cfq";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether keyword should be highlighted in result or not
|
||||||
|
*/
|
||||||
|
public static final String SUGGEST_HIGHLIGHT = SUGGEST_PREFIX + "highlight";
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether all terms are required or not
|
||||||
|
*/
|
||||||
|
public static final String SUGGEST_ALL_TERMS_REQUIRED = SUGGEST_PREFIX + "allTermsRequired";
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -75,6 +75,11 @@ public class AnalyzingInfixLookupFactory extends LookupFactory {
|
||||||
* File name for the automaton.
|
* File name for the automaton.
|
||||||
*/
|
*/
|
||||||
private static final String FILENAME = "iwfsta.bin";
|
private static final String FILENAME = "iwfsta.bin";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clone of CONTEXTS_FIELD_NAME in AnalyzingInfixSuggester
|
||||||
|
*/
|
||||||
|
public static final String CONTEXTS_FIELD_NAME = "contexts";
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -110,7 +115,7 @@ public class AnalyzingInfixLookupFactory extends LookupFactory {
|
||||||
|
|
||||||
boolean highlight = params.get(HIGHLIGHT) != null
|
boolean highlight = params.get(HIGHLIGHT) != null
|
||||||
? Boolean.getBoolean(params.get(HIGHLIGHT).toString())
|
? Boolean.getBoolean(params.get(HIGHLIGHT).toString())
|
||||||
: AnalyzingInfixSuggester.DEFAULT_HIGHLIGHT;
|
: AnalyzingInfixSuggester.DEFAULT_HIGHLIGHT;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return new AnalyzingInfixSuggester(FSDirectory.open(new File(indexPath).toPath()), indexAnalyzer,
|
return new AnalyzingInfixSuggester(FSDirectory.open(new File(indexPath).toPath()), indexAnalyzer,
|
||||||
|
|
|
@ -0,0 +1,121 @@
|
||||||
|
<?xml version="1.0" ?>
|
||||||
|
<!--
|
||||||
|
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.
|
||||||
|
-->
|
||||||
|
<config>
|
||||||
|
<xi:include href="solrconfig.snippet.randomindexconfig.xml" xmlns:xi="http://www.w3.org/2001/XInclude"/>
|
||||||
|
<luceneMatchVersion>${tests.luceneMatchVersion:LATEST}</luceneMatchVersion>
|
||||||
|
<!-- The DirectoryFactory to use for indexes.
|
||||||
|
solr.StandardDirectoryFactory, the default, is filesystem based.
|
||||||
|
solr.RAMDirectoryFactory is memory based and not persistent. -->
|
||||||
|
<dataDir>${solr.data.dir:}</dataDir>
|
||||||
|
<directoryFactory name="DirectoryFactory" class="solr.NRTCachingDirectoryFactory"/>
|
||||||
|
|
||||||
|
<updateHandler class="solr.DirectUpdateHandler2"/>
|
||||||
|
|
||||||
|
<requestHandler name="standard" class="solr.StandardRequestHandler" />
|
||||||
|
|
||||||
|
<searchComponent class="solr.SuggestComponent" name="suggest">
|
||||||
|
<!--Suggest Component for context filtering test -->
|
||||||
|
<lst name="suggester">
|
||||||
|
<str name="name">suggest_blended_infix_suggester</str>
|
||||||
|
<str name="lookupImpl">BlendedInfixLookupFactory</str>
|
||||||
|
<str name="dictionaryImpl">DocumentDictionaryFactory</str>
|
||||||
|
<str name="field">cat</str>
|
||||||
|
<str name="weightField">price</str>
|
||||||
|
<str name="contextField">my_contexts_t</str>
|
||||||
|
<str name="suggestAnalyzerFieldType">text</str>
|
||||||
|
<str name="buildOnCommit">false</str>
|
||||||
|
<str name="buildOnStartup">false</str>
|
||||||
|
<str name="storeDir">suggest_blended_infix_suggester</str>
|
||||||
|
<str name="indexPath">suggest_blended_infix_suggester</str>
|
||||||
|
<str name="highlight">false</str>
|
||||||
|
</lst>
|
||||||
|
|
||||||
|
<lst name="suggester">
|
||||||
|
<str name="name">suggest_blended_infix_suggester_string</str>
|
||||||
|
<str name="lookupImpl">BlendedInfixLookupFactory</str>
|
||||||
|
<str name="dictionaryImpl">DocumentDictionaryFactory</str>
|
||||||
|
<str name="field">cat</str>
|
||||||
|
<str name="weightField">price</str>
|
||||||
|
<str name="contextField">my_contexts_s</str>
|
||||||
|
<str name="suggestAnalyzerFieldType">text</str>
|
||||||
|
<str name="buildOnCommit">false</str>
|
||||||
|
<str name="buildOnStartup">false</str>
|
||||||
|
<str name="storeDir">suggest_blended_infix_suggester_string</str>
|
||||||
|
<str name="indexPath">suggest_blended_infix_suggester_string</str>
|
||||||
|
<str name="highlight">false</str>
|
||||||
|
</lst>
|
||||||
|
|
||||||
|
<lst name="suggester">
|
||||||
|
<str name="name">suggest_lookup_has_no_context_implementation</str>
|
||||||
|
<str name="lookupImpl">AnalyzingLookupFactory</str>
|
||||||
|
<str name="dictionaryImpl">DocumentDictionaryFactory</str>
|
||||||
|
<str name="field">cat</str>
|
||||||
|
<str name="weightField">price</str>
|
||||||
|
<str name="suggestAnalyzerFieldType">text</str>
|
||||||
|
<str name="buildOnCommit">false</str>
|
||||||
|
<str name="buildOnStartup">false</str>
|
||||||
|
<str name="storeDir">suggest_lookup_has_no_context_implementation</str>
|
||||||
|
<str name="indexPath">suggest_lookup_has_no_context_implementation</str>
|
||||||
|
<str name="highlight">false</str>
|
||||||
|
</lst>
|
||||||
|
|
||||||
|
<lst name="suggester">
|
||||||
|
<str name="name">suggest_context_filtering_not_implemented</str>
|
||||||
|
<str name="lookupImpl">AnalyzingLookupFactory</str>
|
||||||
|
<str name="dictionaryImpl">DocumentDictionaryFactory</str>
|
||||||
|
<str name="field">cat</str>
|
||||||
|
<str name="weightField">price</str>
|
||||||
|
<str name="suggestAnalyzerFieldType">text</str>
|
||||||
|
<str name="buildOnCommit">false</str>
|
||||||
|
<str name="buildOnStartup">false</str>
|
||||||
|
<str name="contextField">my_contexts_t</str>
|
||||||
|
<str name="storeDir">suggest_context_filtering_not_implemented</str>
|
||||||
|
<str name="indexPath">suggest_context_filtering_not_implemented</str>
|
||||||
|
<str name="highlight">false</str>
|
||||||
|
</lst>
|
||||||
|
|
||||||
|
|
||||||
|
<lst name="suggester">
|
||||||
|
<str name="name">suggest_context_implemented_but_not_configured</str>
|
||||||
|
<str name="lookupImpl">BlendedInfixLookupFactory</str>
|
||||||
|
<str name="dictionaryImpl">DocumentDictionaryFactory</str>
|
||||||
|
<str name="field">cat</str>
|
||||||
|
<str name="weightField">price</str>
|
||||||
|
<str name="suggestAnalyzerFieldType">text</str>
|
||||||
|
<str name="buildOnCommit">false</str>
|
||||||
|
<str name="buildOnStartup">false</str>
|
||||||
|
<str name="storeDir">suggest_context_implemented_but_not_configured</str>
|
||||||
|
<str name="indexPath">suggest_context_implemented_but_not_configured</str>
|
||||||
|
<str name="highlight">false</str>
|
||||||
|
</lst>
|
||||||
|
|
||||||
|
</searchComponent>
|
||||||
|
<requestHandler name="/suggest" class="org.apache.solr.handler.component.SearchHandler">
|
||||||
|
<lst name="defaults">
|
||||||
|
<str name="suggest">true</str>
|
||||||
|
<str name="suggest.count">5</str>
|
||||||
|
</lst>
|
||||||
|
<arr name="components">
|
||||||
|
<str>suggest</str>
|
||||||
|
</arr>
|
||||||
|
</requestHandler>
|
||||||
|
|
||||||
|
<query><useColdSearcher>false</useColdSearcher></query>
|
||||||
|
|
||||||
|
</config>
|
||||||
|
|
|
@ -0,0 +1,258 @@
|
||||||
|
package org.apache.solr.handler.component;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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 org.apache.solr.SolrTestCaseJ4;
|
||||||
|
import org.apache.solr.spelling.suggest.SuggesterParams;
|
||||||
|
import org.junit.BeforeClass;
|
||||||
|
import org.junit.Ignore;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import static org.hamcrest.core.Is.is;
|
||||||
|
|
||||||
|
public class SuggestComponentContextFilterQueryTest extends SolrTestCaseJ4 {
|
||||||
|
|
||||||
|
static String rh = "/suggest";
|
||||||
|
|
||||||
|
@BeforeClass
|
||||||
|
public static void beforeClass() throws Exception {
|
||||||
|
initCore("solrconfig-suggestercomponent-context-filter-query.xml", "schema.xml");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setUp() throws Exception {
|
||||||
|
super.setUp();
|
||||||
|
|
||||||
|
assertU(delQ("*:*"));
|
||||||
|
// id, cat, price, weight, contexts
|
||||||
|
assertU(adoc("id", "0", "cat", "This is a title", "price", "5", "weight", "10", "my_contexts_t", "ctx1"));
|
||||||
|
assertU(adoc("id", "1", "cat", "This is another title", "price", "10", "weight", "10", "my_contexts_t", "ctx1"));
|
||||||
|
assertU(adoc("id", "7", "cat", "example with ctx1 at 40", "price", "40", "weight", "30", "my_contexts_t", "ctx1"));
|
||||||
|
assertU(adoc("id", "8", "cat", "example with ctx2 and ctx3 at 45", "price", "45", "weight", "30", "my_contexts_t", "CTX2", "my_contexts_t", "CTX3"));
|
||||||
|
assertU(adoc("id", "9", "cat", "example with ctx4 at 50 using my_contexts_s", "price", "50", "weight", "40", "my_contexts_s", "ctx4"));
|
||||||
|
assertU((commit()));
|
||||||
|
waitForWarming();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testContextFilterParamIsIgnoredWhenContextIsNotImplemented() throws Exception {
|
||||||
|
assertQ(req("qt", rh,
|
||||||
|
SuggesterParams.SUGGEST_BUILD, "true",
|
||||||
|
SuggesterParams.SUGGEST_DICT, "suggest_lookup_has_no_context_implementation",
|
||||||
|
SuggesterParams.SUGGEST_CONTEXT_FILTER_QUERY, "ctx1",
|
||||||
|
SuggesterParams.SUGGEST_Q, "examp"),
|
||||||
|
"//lst[@name='suggest']/lst[@name='suggest_lookup_has_no_context_implementation']/lst[@name='examp']/int[@name='numFound'][.='3']",
|
||||||
|
"//lst[@name='suggest']/lst[@name='suggest_lookup_has_no_context_implementation']/lst[@name='examp']/arr[@name='suggestions']/lst[1]/str[@name='term'][.='example with ctx4 at 50 using my_contexts_s']",
|
||||||
|
"//lst[@name='suggest']/lst[@name='suggest_lookup_has_no_context_implementation']/lst[@name='examp']/arr[@name='suggestions']/lst[2]/str[@name='term'][.='example with ctx2 and ctx3 at 45']",
|
||||||
|
"//lst[@name='suggest']/lst[@name='suggest_lookup_has_no_context_implementation']/lst[@name='examp']/arr[@name='suggestions']/lst[3]/str[@name='term'][.='example with ctx1 at 40']"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testContextFilteringIsIgnoredWhenContextIsImplementedButNotConfigured() throws Exception {
|
||||||
|
assertQ(req("qt", rh,
|
||||||
|
SuggesterParams.SUGGEST_BUILD, "true",
|
||||||
|
SuggesterParams.SUGGEST_DICT, "suggest_context_implemented_but_not_configured",
|
||||||
|
SuggesterParams.SUGGEST_Q, "examp"),
|
||||||
|
"//lst[@name='suggest']/lst[@name='suggest_context_implemented_but_not_configured']/lst[@name='examp']/int[@name='numFound'][.='3']",
|
||||||
|
"//lst[@name='suggest']/lst[@name='suggest_context_implemented_but_not_configured']/lst[@name='examp']/arr[@name='suggestions']/lst[1]/str[@name='term'][.='example with ctx4 at 50 using my_contexts_s']",
|
||||||
|
"//lst[@name='suggest']/lst[@name='suggest_context_implemented_but_not_configured']/lst[@name='examp']/arr[@name='suggestions']/lst[2]/str[@name='term'][.='example with ctx2 and ctx3 at 45']",
|
||||||
|
"//lst[@name='suggest']/lst[@name='suggest_context_implemented_but_not_configured']/lst[@name='examp']/arr[@name='suggestions']/lst[3]/str[@name='term'][.='example with ctx1 at 40']"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testBuildThrowsIllegalArgumentExceptionWhenContextIsConfiguredButNotImplemented() throws Exception {
|
||||||
|
try {
|
||||||
|
assertQ(
|
||||||
|
req("qt", rh,
|
||||||
|
SuggesterParams.SUGGEST_BUILD, "true",
|
||||||
|
SuggesterParams.SUGGEST_DICT, "suggest_context_filtering_not_implemented",
|
||||||
|
SuggesterParams.SUGGEST_Q, "examp")
|
||||||
|
,
|
||||||
|
""
|
||||||
|
);
|
||||||
|
fail("Expecting exception because ");
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
Throwable cause = e.getCause();
|
||||||
|
assertTrue(cause instanceof IllegalArgumentException);
|
||||||
|
assertThat(cause.getMessage(), is("this suggester doesn't support contexts"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// When not building, no exception is thrown
|
||||||
|
assertQ(req("qt", rh,
|
||||||
|
SuggesterParams.SUGGEST_BUILD, "false",
|
||||||
|
SuggesterParams.SUGGEST_DICT, "suggest_context_filtering_not_implemented",
|
||||||
|
SuggesterParams.SUGGEST_Q, "examp"),
|
||||||
|
"//lst[@name='suggest']/lst[@name='suggest_context_filtering_not_implemented']/lst[@name='examp']/int[@name='numFound'][.='0']"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testContextFilterIsTrimmed() throws Exception {
|
||||||
|
assertQ(req("qt", rh,
|
||||||
|
SuggesterParams.SUGGEST_BUILD, "true",
|
||||||
|
SuggesterParams.SUGGEST_DICT, "suggest_blended_infix_suggester",
|
||||||
|
SuggesterParams.SUGGEST_CONTEXT_FILTER_QUERY, " ", //trimmed to null... just as if there was no context filter param
|
||||||
|
SuggesterParams.SUGGEST_Q, "examp"),
|
||||||
|
"//lst[@name='suggest']/lst[@name='suggest_blended_infix_suggester']/lst[@name='examp']/int[@name='numFound'][.='3']"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testExplicitFieldedQuery() throws Exception {
|
||||||
|
assertQ(req("qt", rh,
|
||||||
|
SuggesterParams.SUGGEST_BUILD, "true",
|
||||||
|
SuggesterParams.SUGGEST_DICT, "suggest_blended_infix_suggester",
|
||||||
|
SuggesterParams.SUGGEST_CONTEXT_FILTER_QUERY, "contexts:ctx1",
|
||||||
|
SuggesterParams.SUGGEST_Q, "examp"),
|
||||||
|
"//lst[@name='suggest']/lst[@name='suggest_blended_infix_suggester']/lst[@name='examp']/int[@name='numFound'][.='1']",
|
||||||
|
"//lst[@name='suggest']/lst[@name='suggest_blended_infix_suggester']/lst[@name='examp']/arr[@name='suggestions']/lst[1]/str[@name='term'][.='example with ctx1 at 40']"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testContextFilterOK() throws Exception {
|
||||||
|
//No filtering
|
||||||
|
assertQ(req("qt", rh,
|
||||||
|
SuggesterParams.SUGGEST_BUILD, "true",
|
||||||
|
SuggesterParams.SUGGEST_DICT, "suggest_blended_infix_suggester",
|
||||||
|
SuggesterParams.SUGGEST_Q, "examp"),
|
||||||
|
"//lst[@name='suggest']/lst[@name='suggest_blended_infix_suggester']/lst[@name='examp']/int[@name='numFound'][.='3']",
|
||||||
|
"//lst[@name='suggest']/lst[@name='suggest_blended_infix_suggester']/lst[@name='examp']/arr[@name='suggestions']/lst[1]/str[@name='term'][.='example with ctx4 at 50 using my_contexts_s']",
|
||||||
|
"//lst[@name='suggest']/lst[@name='suggest_blended_infix_suggester']/lst[@name='examp']/arr[@name='suggestions']/lst[2]/str[@name='term'][.='example with ctx2 and ctx3 at 45']",
|
||||||
|
"//lst[@name='suggest']/lst[@name='suggest_blended_infix_suggester']/lst[@name='examp']/arr[@name='suggestions']/lst[3]/str[@name='term'][.='example with ctx1 at 40']"
|
||||||
|
);
|
||||||
|
|
||||||
|
//TermQuery
|
||||||
|
assertQ(req("qt", rh,
|
||||||
|
SuggesterParams.SUGGEST_BUILD, "true",
|
||||||
|
SuggesterParams.SUGGEST_DICT, "suggest_blended_infix_suggester",
|
||||||
|
SuggesterParams.SUGGEST_CONTEXT_FILTER_QUERY, "ctx1",
|
||||||
|
SuggesterParams.SUGGEST_Q, "examp"),
|
||||||
|
"//lst[@name='suggest']/lst[@name='suggest_blended_infix_suggester']/lst[@name='examp']/int[@name='numFound'][.='1']",
|
||||||
|
"//lst[@name='suggest']/lst[@name='suggest_blended_infix_suggester']/lst[@name='examp']/arr[@name='suggestions']/lst[1]/str[@name='term'][.='example with ctx1 at 40']"
|
||||||
|
);
|
||||||
|
|
||||||
|
//OR BooleanQuery
|
||||||
|
assertQ(req("qt", rh,
|
||||||
|
SuggesterParams.SUGGEST_BUILD, "true",
|
||||||
|
SuggesterParams.SUGGEST_DICT, "suggest_blended_infix_suggester",
|
||||||
|
SuggesterParams.SUGGEST_CONTEXT_FILTER_QUERY, "ctx1 OR CTX2",
|
||||||
|
SuggesterParams.SUGGEST_Q, "examp"),
|
||||||
|
"//lst[@name='suggest']/lst[@name='suggest_blended_infix_suggester']/lst[@name='examp']/int[@name='numFound'][.='2']",
|
||||||
|
"//lst[@name='suggest']/lst[@name='suggest_blended_infix_suggester']/lst[@name='examp']/arr[@name='suggestions']/lst[1]/str[@name='term'][.='example with ctx2 and ctx3 at 45']",
|
||||||
|
"//lst[@name='suggest']/lst[@name='suggest_blended_infix_suggester']/lst[@name='examp']/arr[@name='suggestions']/lst[2]/str[@name='term'][.='example with ctx1 at 40']"
|
||||||
|
);
|
||||||
|
|
||||||
|
//AND BooleanQuery
|
||||||
|
assertQ(req("qt", rh,
|
||||||
|
SuggesterParams.SUGGEST_BUILD, "true",
|
||||||
|
SuggesterParams.SUGGEST_DICT, "suggest_blended_infix_suggester",
|
||||||
|
SuggesterParams.SUGGEST_CONTEXT_FILTER_QUERY, "CTX2 AND CTX3",
|
||||||
|
SuggesterParams.SUGGEST_Q, "examp"),
|
||||||
|
"//lst[@name='suggest']/lst[@name='suggest_blended_infix_suggester']/lst[@name='examp']/int[@name='numFound'][.='1']",
|
||||||
|
"//lst[@name='suggest']/lst[@name='suggest_blended_infix_suggester']/lst[@name='examp']/arr[@name='suggestions']/lst[1]/str[@name='term'][.='example with ctx2 and ctx3 at 45']");
|
||||||
|
|
||||||
|
|
||||||
|
//PrefixQuery
|
||||||
|
assertQ(req("qt", rh,
|
||||||
|
SuggesterParams.SUGGEST_BUILD, "true",
|
||||||
|
SuggesterParams.SUGGEST_DICT, "suggest_blended_infix_suggester",
|
||||||
|
SuggesterParams.SUGGEST_CONTEXT_FILTER_QUERY, "ctx*",
|
||||||
|
SuggesterParams.SUGGEST_Q, "examp"),
|
||||||
|
"//lst[@name='suggest']/lst[@name='suggest_blended_infix_suggester']/lst[@name='examp']/int[@name='numFound'][.='1']",
|
||||||
|
"//lst[@name='suggest']/lst[@name='suggest_blended_infix_suggester']/lst[@name='examp']/arr[@name='suggestions']/lst[1]/str[@name='term'][.='example with ctx1 at 40']"
|
||||||
|
);
|
||||||
|
|
||||||
|
//RangeQuery
|
||||||
|
assertQ(req("qt", rh,
|
||||||
|
SuggesterParams.SUGGEST_BUILD, "true",
|
||||||
|
SuggesterParams.SUGGEST_DICT, "suggest_blended_infix_suggester",
|
||||||
|
SuggesterParams.SUGGEST_CONTEXT_FILTER_QUERY, "[* TO *]",
|
||||||
|
SuggesterParams.SUGGEST_Q, "examp"),
|
||||||
|
"//lst[@name='suggest']/lst[@name='suggest_blended_infix_suggester']/lst[@name='examp']/int[@name='numFound'][.='2']",
|
||||||
|
"//lst[@name='suggest']/lst[@name='suggest_blended_infix_suggester']/lst[@name='examp']/arr[@name='suggestions']/lst[1]/str[@name='term'][.='example with ctx2 and ctx3 at 45']",
|
||||||
|
"//lst[@name='suggest']/lst[@name='suggest_blended_infix_suggester']/lst[@name='examp']/arr[@name='suggestions']/lst[2]/str[@name='term'][.='example with ctx1 at 40']"
|
||||||
|
);
|
||||||
|
|
||||||
|
//WildcardQuery
|
||||||
|
assertQ(req("qt", rh,
|
||||||
|
SuggesterParams.SUGGEST_BUILD, "true",
|
||||||
|
SuggesterParams.SUGGEST_DICT, "suggest_blended_infix_suggester",
|
||||||
|
SuggesterParams.SUGGEST_CONTEXT_FILTER_QUERY, "c*1",
|
||||||
|
SuggesterParams.SUGGEST_Q, "examp"),
|
||||||
|
"//lst[@name='suggest']/lst[@name='suggest_blended_infix_suggester']/lst[@name='examp']/int[@name='numFound'][.='1']",
|
||||||
|
"//lst[@name='suggest']/lst[@name='suggest_blended_infix_suggester']/lst[@name='examp']/arr[@name='suggestions']/lst[1]/str[@name='term'][.='example with ctx1 at 40']");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testStringContext(){
|
||||||
|
//Here, the context field is a string, so it's case sensitive
|
||||||
|
assertQ(req("qt", rh,
|
||||||
|
SuggesterParams.SUGGEST_BUILD, "true",
|
||||||
|
SuggesterParams.SUGGEST_DICT, "suggest_blended_infix_suggester_string",
|
||||||
|
SuggesterParams.SUGGEST_CONTEXT_FILTER_QUERY, "Ctx4",
|
||||||
|
SuggesterParams.SUGGEST_Q, "examp"),
|
||||||
|
"//lst[@name='suggest']/lst[@name='suggest_blended_infix_suggester_string']/lst[@name='examp']/int[@name='numFound'][.='0']");
|
||||||
|
|
||||||
|
assertQ(req("qt", rh,
|
||||||
|
SuggesterParams.SUGGEST_BUILD, "true",
|
||||||
|
SuggesterParams.SUGGEST_DICT, "suggest_blended_infix_suggester_string",
|
||||||
|
SuggesterParams.SUGGEST_CONTEXT_FILTER_QUERY, "ctx4",
|
||||||
|
SuggesterParams.SUGGEST_Q, "examp"),
|
||||||
|
"//lst[@name='suggest']/lst[@name='suggest_blended_infix_suggester_string']/lst[@name='examp']/int[@name='numFound'][.='1']");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testContextFilterOnInvalidFieldGivesNoSuggestions() throws Exception {
|
||||||
|
assertQ(req("qt", rh,
|
||||||
|
SuggesterParams.SUGGEST_BUILD, "true",
|
||||||
|
SuggesterParams.SUGGEST_DICT, "suggest_blended_infix_suggester",
|
||||||
|
SuggesterParams.SUGGEST_CONTEXT_FILTER_QUERY, "some_invalid_context_field:some_invalid_value",
|
||||||
|
SuggesterParams.SUGGEST_Q, "examp"),
|
||||||
|
"//lst[@name='suggest']/lst[@name='suggest_blended_infix_suggester']/lst[@name='examp']/int[@name='numFound'][.='0']");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testContextFilterUsesAnalyzer() throws Exception {
|
||||||
|
assertQ(req("qt", rh,
|
||||||
|
SuggesterParams.SUGGEST_BUILD, "true",
|
||||||
|
SuggesterParams.SUGGEST_DICT, "suggest_blended_infix_suggester",
|
||||||
|
SuggesterParams.SUGGEST_CONTEXT_FILTER_QUERY, "CTx1", // Will not match due to case
|
||||||
|
SuggesterParams.SUGGEST_Q, "examp"),
|
||||||
|
"//lst[@name='suggest']/lst[@name='suggest_blended_infix_suggester']/lst[@name='examp']/int[@name='numFound'][.='0']");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Ignore// TODO: SOLR-7964
|
||||||
|
@Test
|
||||||
|
public void testContextFilterWithHighlight() throws Exception {
|
||||||
|
assertQ(req("qt", rh,
|
||||||
|
SuggesterParams.SUGGEST_BUILD, "true",
|
||||||
|
SuggesterParams.SUGGEST_DICT, "suggest_blended_infix_suggester",
|
||||||
|
SuggesterParams.SUGGEST_CONTEXT_FILTER_QUERY, "ctx1",
|
||||||
|
SuggesterParams.SUGGEST_HIGHLIGHT, "true",
|
||||||
|
SuggesterParams.SUGGEST_Q, "example"),
|
||||||
|
"//lst[@name='suggest']/lst[@name='suggest_blended_infix_suggester']/lst[@name='example']/int[@name='numFound'][.='1']",
|
||||||
|
"//lst[@name='suggest']/lst[@name='suggest_blended_infix_suggester']/lst[@name='example']/arr[@name='suggestions']/lst[1]/str[@name='term'][.='<b>example</b> data']"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -522,20 +522,4 @@ public class SuggestComponentTest extends SolrTestCaseJ4 {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void waitForWarming() throws InterruptedException {
|
|
||||||
RefCounted<SolrIndexSearcher> registeredSearcher = h.getCore().getRegisteredSearcher();
|
|
||||||
RefCounted<SolrIndexSearcher> newestSearcher = h.getCore().getNewestSearcher(false);;
|
|
||||||
while (registeredSearcher == null || registeredSearcher.get() != newestSearcher.get()) {
|
|
||||||
if (registeredSearcher != null) {
|
|
||||||
registeredSearcher.decref();
|
|
||||||
}
|
|
||||||
newestSearcher.decref();
|
|
||||||
Thread.sleep(50);
|
|
||||||
registeredSearcher = h.getCore().getRegisteredSearcher();
|
|
||||||
newestSearcher = h.getCore().getNewestSearcher(false);
|
|
||||||
}
|
|
||||||
registeredSearcher.decref();
|
|
||||||
newestSearcher.decref();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -98,6 +98,7 @@ import org.apache.solr.search.SolrIndexSearcher;
|
||||||
import org.apache.solr.servlet.DirectSolrConnection;
|
import org.apache.solr.servlet.DirectSolrConnection;
|
||||||
import org.apache.solr.util.AbstractSolrTestCase;
|
import org.apache.solr.util.AbstractSolrTestCase;
|
||||||
import org.apache.solr.util.DateFormatUtil;
|
import org.apache.solr.util.DateFormatUtil;
|
||||||
|
import org.apache.solr.util.RefCounted;
|
||||||
import org.apache.solr.util.RevertDefaultThreadHandlerRule;
|
import org.apache.solr.util.RevertDefaultThreadHandlerRule;
|
||||||
import org.apache.solr.util.SSLTestConfig;
|
import org.apache.solr.util.SSLTestConfig;
|
||||||
import org.apache.solr.util.TestHarness;
|
import org.apache.solr.util.TestHarness;
|
||||||
|
@ -2117,4 +2118,20 @@ public abstract class SolrTestCaseJ4 extends LuceneTestCase {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void waitForWarming() throws InterruptedException {
|
||||||
|
RefCounted<SolrIndexSearcher> registeredSearcher = h.getCore().getRegisteredSearcher();
|
||||||
|
RefCounted<SolrIndexSearcher> newestSearcher = h.getCore().getNewestSearcher(false);
|
||||||
|
;
|
||||||
|
while (registeredSearcher == null || registeredSearcher.get() != newestSearcher.get()) {
|
||||||
|
if (registeredSearcher != null) {
|
||||||
|
registeredSearcher.decref();
|
||||||
|
}
|
||||||
|
newestSearcher.decref();
|
||||||
|
Thread.sleep(50);
|
||||||
|
registeredSearcher = h.getCore().getRegisteredSearcher();
|
||||||
|
newestSearcher = h.getCore().getNewestSearcher(false);
|
||||||
|
}
|
||||||
|
registeredSearcher.decref();
|
||||||
|
newestSearcher.decref();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue