You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by ja...@apache.org on 2015/10/10 21:01:59 UTC
svn commit: r1707907 - in /lucene/dev/trunk:
lucene/suggest/src/java/org/apache/lucene/search/suggest/ solr/
solr/core/src/java/org/apache/solr/handler/component/
solr/core/src/java/org/apache/solr/spelling/suggest/
solr/core/src/java/org/apache/solr/s...
Author: janhoy
Date: Sat Oct 10 19:01:59 2015
New Revision: 1707907
URL: http://svn.apache.org/viewvc?rev=1707907&view=rev
Log:
SOLR-7888: Analyzing suggesters can now filter suggestions by a context field
Added:
lucene/dev/trunk/solr/core/src/test-files/solr/collection1/conf/solrconfig-suggestercomponent-context-filter-query.xml (with props)
lucene/dev/trunk/solr/core/src/test/org/apache/solr/handler/component/SuggestComponentContextFilterQueryTest.java (with props)
Modified:
lucene/dev/trunk/lucene/suggest/src/java/org/apache/lucene/search/suggest/Lookup.java
lucene/dev/trunk/solr/CHANGES.txt
lucene/dev/trunk/solr/core/src/java/org/apache/solr/handler/component/SuggestComponent.java
lucene/dev/trunk/solr/core/src/java/org/apache/solr/spelling/suggest/DocumentDictionaryFactory.java
lucene/dev/trunk/solr/core/src/java/org/apache/solr/spelling/suggest/SolrSuggester.java
lucene/dev/trunk/solr/core/src/java/org/apache/solr/spelling/suggest/SuggesterOptions.java
lucene/dev/trunk/solr/core/src/java/org/apache/solr/spelling/suggest/SuggesterParams.java
lucene/dev/trunk/solr/core/src/java/org/apache/solr/spelling/suggest/fst/AnalyzingInfixLookupFactory.java
lucene/dev/trunk/solr/core/src/test/org/apache/solr/handler/component/SuggestComponentTest.java
lucene/dev/trunk/solr/test-framework/src/java/org/apache/solr/SolrTestCaseJ4.java
Modified: lucene/dev/trunk/lucene/suggest/src/java/org/apache/lucene/search/suggest/Lookup.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/suggest/src/java/org/apache/lucene/search/suggest/Lookup.java?rev=1707907&r1=1707906&r2=1707907&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/suggest/src/java/org/apache/lucene/search/suggest/Lookup.java (original)
+++ lucene/dev/trunk/lucene/suggest/src/java/org/apache/lucene/search/suggest/Lookup.java Sat Oct 10 19:01:59 2015
@@ -24,6 +24,7 @@ import java.util.Comparator;
import java.util.List;
import java.util.Set;
+import org.apache.lucene.search.BooleanQuery;
import org.apache.lucene.search.spell.Dictionary;
import org.apache.lucene.store.DataInput;
import org.apache.lucene.store.DataOutput;
@@ -252,6 +253,22 @@ public abstract class Lookup implements
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.
* @param output {@link DataOutput} to write the data to.
* @return true if successful, false if unsuccessful or not supported.
Modified: lucene/dev/trunk/solr/CHANGES.txt
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/CHANGES.txt?rev=1707907&r1=1707906&r2=1707907&view=diff
==============================================================================
--- lucene/dev/trunk/solr/CHANGES.txt (original)
+++ lucene/dev/trunk/solr/CHANGES.txt Sat Oct 10 19:01:59 2015
@@ -176,6 +176,8 @@ New Features
* 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
----------------------
Modified: lucene/dev/trunk/solr/core/src/java/org/apache/solr/handler/component/SuggestComponent.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/core/src/java/org/apache/solr/handler/component/SuggestComponent.java?rev=1707907&r1=1707906&r2=1707907&view=diff
==============================================================================
--- lucene/dev/trunk/solr/core/src/java/org/apache/solr/handler/component/SuggestComponent.java (original)
+++ lucene/dev/trunk/solr/core/src/java/org/apache/solr/handler/component/SuggestComponent.java Sat Oct 10 19:01:59 2015
@@ -186,7 +186,7 @@ public class SuggestComponent extends Se
rb.rsp.add("command", (!reloadAll) ? "reload" : "reloadAll");
}
}
-
+
/** Dispatch shard request in <code>STAGE_EXECUTE_QUERY</code> stage */
@Override
public int distributedProcess(ResponseBuilder rb) {
@@ -238,11 +238,21 @@ public class SuggestComponent extends Se
query = params.get(CommonParams.Q);
}
}
-
+
if (query != null) {
int count = params.getInt(SUGGEST_COUNT, 1);
- SuggesterOptions options = new SuggesterOptions(new CharsRef(query), count);
- Map<String, SimpleOrderedMap<NamedList<Object>>> namedListResults =
+ boolean highlight = params.getBool(SUGGEST_HIGHLIGHT, false);
+ 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<>();
for (SolrSuggester suggester : querySuggesters) {
SuggesterResult suggesterResult = suggester.getSuggestions(options);
@@ -251,7 +261,7 @@ public class SuggestComponent extends Se
rb.rsp.add(SuggesterResultLabels.SUGGEST, namedListResults);
}
}
-
+
/**
* Used in Distributed Search, merges the suggestion results from every shard
* */
Modified: lucene/dev/trunk/solr/core/src/java/org/apache/solr/spelling/suggest/DocumentDictionaryFactory.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/core/src/java/org/apache/solr/spelling/suggest/DocumentDictionaryFactory.java?rev=1707907&r1=1707906&r2=1707907&view=diff
==============================================================================
--- lucene/dev/trunk/solr/core/src/java/org/apache/solr/spelling/suggest/DocumentDictionaryFactory.java (original)
+++ lucene/dev/trunk/solr/core/src/java/org/apache/solr/spelling/suggest/DocumentDictionaryFactory.java Sat Oct 10 19:01:59 2015
@@ -33,6 +33,8 @@ public class DocumentDictionaryFactory e
public static final String PAYLOAD_FIELD = "payloadField";
+ public static final String CONTEXT_FIELD = "contextField";
+
@Override
public Dictionary create(SolrCore core, SolrIndexSearcher searcher) {
if(params == null) {
@@ -42,12 +44,13 @@ public class DocumentDictionaryFactory e
String field = (String) params.get(FIELD);
String weightField = (String) params.get(WEIGHT_FIELD);
String payloadField = (String) params.get(PAYLOAD_FIELD);
-
+ String contextField = (String) params.get(CONTEXT_FIELD);
+
if (field == null) {
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);
}
}
Modified: lucene/dev/trunk/solr/core/src/java/org/apache/solr/spelling/suggest/SolrSuggester.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/core/src/java/org/apache/solr/spelling/suggest/SolrSuggester.java?rev=1707907&r1=1707906&r2=1707907&view=diff
==============================================================================
--- lucene/dev/trunk/solr/core/src/java/org/apache/solr/spelling/suggest/SolrSuggester.java (original)
+++ lucene/dev/trunk/solr/core/src/java/org/apache/solr/spelling/suggest/SolrSuggester.java Sat Oct 10 19:01:59 2015
@@ -23,13 +23,22 @@ import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Collection;
+import java.util.Collections;
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.suggest.Lookup;
import org.apache.lucene.search.suggest.Lookup.LookupResult;
import org.apache.lucene.util.Accountable;
import org.apache.lucene.util.IOUtils;
+import org.apache.solr.analysis.TokenizerChain;
import org.apache.solr.common.util.NamedList;
import org.apache.solr.core.CloseHook;
import org.apache.solr.core.SolrCore;
@@ -38,6 +47,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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
@@ -61,7 +71,7 @@ public class SolrSuggester implements Ac
/** Fully-qualified class of the {@link Dictionary} implementation */
public static final String DICTIONARY_IMPL = "dictionaryImpl";
-
+
/**
* 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
@@ -81,8 +91,9 @@ public class SolrSuggester implements Ac
private LookupFactory factory;
private DictionaryFactory dictionaryFactory;
-
- /**
+ private Analyzer contextFilterQueryAnalyzer;
+
+ /**
* Uses the <code>config</code> and the <code>core</code> to initialize the underlying
* Lucene suggester
* */
@@ -101,6 +112,9 @@ public class SolrSuggester implements Ac
lookupImpl = LookupFactory.DEFAULT_FILE_BASED_DICT;
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
factory = core.getResourceLoader().newInstance(lookupImpl, LookupFactory.class);
lookup = factory.create(config, core);
@@ -146,7 +160,7 @@ public class SolrSuggester implements Ac
DictionaryFactory.DEFAULT_FILE_BASED_DICT;
LOG.info("No " + DICTIONARY_IMPL + " parameter was provided falling back to " + dictionaryImpl);
}
-
+
dictionaryFactory = core.getResourceLoader().newInstance(dictionaryImpl, DictionaryFactory.class);
dictionaryFactory.setParams(config);
LOG.info("Dictionary loaded with params: " + config);
@@ -212,11 +226,41 @@ public class SolrSuggester implements Ac
}
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);
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 */
public String getName() {
return name;
Modified: lucene/dev/trunk/solr/core/src/java/org/apache/solr/spelling/suggest/SuggesterOptions.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/core/src/java/org/apache/solr/spelling/suggest/SuggesterOptions.java?rev=1707907&r1=1707906&r2=1707907&view=diff
==============================================================================
--- lucene/dev/trunk/solr/core/src/java/org/apache/solr/spelling/suggest/SuggesterOptions.java (original)
+++ lucene/dev/trunk/solr/core/src/java/org/apache/solr/spelling/suggest/SuggesterOptions.java Sat Oct 10 19:01:59 2015
@@ -30,9 +30,21 @@ public class SuggesterOptions {
/** Number of suggestions requested */
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.count = count;
+ this.contextFilterQuery = contextFilterQuery;
+ this.allTermsRequired = allTermsRequired;
+ this.highlight = highlight;
}
}
Modified: lucene/dev/trunk/solr/core/src/java/org/apache/solr/spelling/suggest/SuggesterParams.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/core/src/java/org/apache/solr/spelling/suggest/SuggesterParams.java?rev=1707907&r1=1707906&r2=1707907&view=diff
==============================================================================
--- lucene/dev/trunk/solr/core/src/java/org/apache/solr/spelling/suggest/SuggesterParams.java (original)
+++ lucene/dev/trunk/solr/core/src/java/org/apache/solr/spelling/suggest/SuggesterParams.java Sat Oct 10 19:01:59 2015
@@ -66,4 +66,21 @@ public interface SuggesterParams {
* This parameter does not need any suggest dictionary names to be specified
*/
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";
+
}
Modified: lucene/dev/trunk/solr/core/src/java/org/apache/solr/spelling/suggest/fst/AnalyzingInfixLookupFactory.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/core/src/java/org/apache/solr/spelling/suggest/fst/AnalyzingInfixLookupFactory.java?rev=1707907&r1=1707906&r2=1707907&view=diff
==============================================================================
--- lucene/dev/trunk/solr/core/src/java/org/apache/solr/spelling/suggest/fst/AnalyzingInfixLookupFactory.java (original)
+++ lucene/dev/trunk/solr/core/src/java/org/apache/solr/spelling/suggest/fst/AnalyzingInfixLookupFactory.java Sat Oct 10 19:01:59 2015
@@ -75,6 +75,11 @@ public class AnalyzingInfixLookupFactory
* File name for the automaton.
*/
private static final String FILENAME = "iwfsta.bin";
+
+ /**
+ * Clone of CONTEXTS_FIELD_NAME in AnalyzingInfixSuggester
+ */
+ public static final String CONTEXTS_FIELD_NAME = "contexts";
@Override
@@ -110,7 +115,7 @@ public class AnalyzingInfixLookupFactory
boolean highlight = params.get(HIGHLIGHT) != null
? Boolean.getBoolean(params.get(HIGHLIGHT).toString())
- : AnalyzingInfixSuggester.DEFAULT_HIGHLIGHT;
+ : AnalyzingInfixSuggester.DEFAULT_HIGHLIGHT;
try {
return new AnalyzingInfixSuggester(FSDirectory.open(new File(indexPath).toPath()), indexAnalyzer,
Added: lucene/dev/trunk/solr/core/src/test-files/solr/collection1/conf/solrconfig-suggestercomponent-context-filter-query.xml
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/core/src/test-files/solr/collection1/conf/solrconfig-suggestercomponent-context-filter-query.xml?rev=1707907&view=auto
==============================================================================
--- lucene/dev/trunk/solr/core/src/test-files/solr/collection1/conf/solrconfig-suggestercomponent-context-filter-query.xml (added)
+++ lucene/dev/trunk/solr/core/src/test-files/solr/collection1/conf/solrconfig-suggestercomponent-context-filter-query.xml Sat Oct 10 19:01:59 2015
@@ -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>
+
Added: lucene/dev/trunk/solr/core/src/test/org/apache/solr/handler/component/SuggestComponentContextFilterQueryTest.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/core/src/test/org/apache/solr/handler/component/SuggestComponentContextFilterQueryTest.java?rev=1707907&view=auto
==============================================================================
--- lucene/dev/trunk/solr/core/src/test/org/apache/solr/handler/component/SuggestComponentContextFilterQueryTest.java (added)
+++ lucene/dev/trunk/solr/core/src/test/org/apache/solr/handler/component/SuggestComponentContextFilterQueryTest.java Sat Oct 10 19:01:59 2015
@@ -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']"
+ );
+ }
+
+}
+
Modified: lucene/dev/trunk/solr/core/src/test/org/apache/solr/handler/component/SuggestComponentTest.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/core/src/test/org/apache/solr/handler/component/SuggestComponentTest.java?rev=1707907&r1=1707906&r2=1707907&view=diff
==============================================================================
--- lucene/dev/trunk/solr/core/src/test/org/apache/solr/handler/component/SuggestComponentTest.java (original)
+++ lucene/dev/trunk/solr/core/src/test/org/apache/solr/handler/component/SuggestComponentTest.java Sat Oct 10 19:01:59 2015
@@ -522,20 +522,4 @@ public class SuggestComponentTest extend
);
}
- 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();
- }
-
}
Modified: lucene/dev/trunk/solr/test-framework/src/java/org/apache/solr/SolrTestCaseJ4.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/test-framework/src/java/org/apache/solr/SolrTestCaseJ4.java?rev=1707907&r1=1707906&r2=1707907&view=diff
==============================================================================
--- lucene/dev/trunk/solr/test-framework/src/java/org/apache/solr/SolrTestCaseJ4.java (original)
+++ lucene/dev/trunk/solr/test-framework/src/java/org/apache/solr/SolrTestCaseJ4.java Sat Oct 10 19:01:59 2015
@@ -98,6 +98,7 @@ import org.apache.solr.search.SolrIndexS
import org.apache.solr.servlet.DirectSolrConnection;
import org.apache.solr.util.AbstractSolrTestCase;
import org.apache.solr.util.DateFormatUtil;
+import org.apache.solr.util.RefCounted;
import org.apache.solr.util.RevertDefaultThreadHandlerRule;
import org.apache.solr.util.SSLTestConfig;
import org.apache.solr.util.TestHarness;
@@ -2117,4 +2118,20 @@ public abstract class SolrTestCaseJ4 ext
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();
+ }
}