mirror of https://github.com/apache/lucene.git
enables multi-param for several dismax params (SOLR-174)
git-svn-id: https://svn.apache.org/repos/asf/lucene/solr/trunk@514851 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
cfb9f5f2f2
commit
25227a299d
|
@ -137,6 +137,12 @@ Changes in runtime behavior
|
|||
IndexSchema have also been clarified.
|
||||
(Erik Hatcher and hossman)
|
||||
|
||||
4. DisMaxRequestHandler's bq, bf, qf, and pf parameters can now accept
|
||||
multiple values (klaas).
|
||||
|
||||
5. Query are re-written before highlighting is performed. This enables
|
||||
proper highlighting of prefix and wildcard queries (klaas).
|
||||
|
||||
Optimizations
|
||||
1. SOLR-114: HashDocSet specific implementations of union() and andNot()
|
||||
for a 20x performance improvement for those set operations, and a new
|
||||
|
|
|
@ -73,6 +73,8 @@ import org.apache.solr.util.SolrPluginUtils;
|
|||
* <li> qf - (Query Fields) fields and boosts to use when building
|
||||
* DisjunctionMaxQueries from the users query. Format is:
|
||||
* "<code>fieldA^1.0 fieldB^2.2</code>".
|
||||
* This param can be specified multiple times, and the fields
|
||||
* are additive.
|
||||
* </li>
|
||||
* <li> mm - (Minimum Match) this supports a wide variety of
|
||||
* complex expressions.
|
||||
|
@ -81,6 +83,8 @@ import org.apache.solr.util.SolrPluginUtils;
|
|||
* <li> pf - (Phrase Fields) fields/boosts to make phrase queries out
|
||||
* of, to boost the users query for exact matches on the specified fields.
|
||||
* Format is: "<code>fieldA^1.0 fieldB^2.2</code>".
|
||||
* This param can be specified multiple times, and the fields
|
||||
* are additive.
|
||||
* </li>
|
||||
* <li> ps - (Phrase Slop) amount of slop on phrase queries built for pf
|
||||
* fields.
|
||||
|
@ -93,12 +97,19 @@ import org.apache.solr.util.SolrPluginUtils;
|
|||
* with a default boost (1.0f), then the individual clauses will be
|
||||
* added directly to the main query. Otherwise, the query will be
|
||||
* included as is.
|
||||
* This param can be specified multiple times, and the boosts are
|
||||
* are additive. NOTE: the behaviour listed above is only in effect
|
||||
* if a single <code>bq</code> paramter is specified. Hence you can
|
||||
* disable it by specifying an additional, blank, <code>bq</code>
|
||||
* parameter.
|
||||
* </li>
|
||||
* <li> bf - (Boost Functions) functions (with optional boosts) that will be
|
||||
* included in the users query to influence the score.
|
||||
* Format is: "<code>funcA(arg1,arg2)^1.2
|
||||
* funcB(arg3,arg4)^2.2</code>". NOTE: Whitespace is not allowed
|
||||
* in the function arguments.
|
||||
* This param can be specified multiple times, and the functions
|
||||
* are additive.
|
||||
* </li>
|
||||
* <li> fq - (Filter Query) a raw lucene query that can be used
|
||||
* to restrict the super set of products we are interested in - more
|
||||
|
@ -122,7 +133,6 @@ import org.apache.solr.util.SolrPluginUtils;
|
|||
* <pre>
|
||||
* :TODO: document facet param support
|
||||
*
|
||||
* :TODO: make bf,pf,qf multival params now that SolrParams supports them
|
||||
* </pre>
|
||||
*/
|
||||
public class DisMaxRequestHandler extends RequestHandlerBase {
|
||||
|
@ -173,8 +183,8 @@ public class DisMaxRequestHandler extends RequestHandlerBase {
|
|||
SolrIndexSearcher s = req.getSearcher();
|
||||
IndexSchema schema = req.getSchema();
|
||||
|
||||
Map<String,Float> queryFields = U.parseFieldBoosts(params.get(DMP.QF));
|
||||
Map<String,Float> phraseFields = U.parseFieldBoosts(params.get(DMP.PF));
|
||||
Map<String,Float> queryFields = U.parseFieldBoosts(params.getParams(DMP.QF));
|
||||
Map<String,Float> phraseFields = U.parseFieldBoosts(params.getParams(DMP.PF));
|
||||
|
||||
float tiebreaker = params.getFloat(DMP.TIE, 0.0f);
|
||||
|
||||
|
@ -255,29 +265,39 @@ public class DisMaxRequestHandler extends RequestHandlerBase {
|
|||
|
||||
|
||||
/* * * Boosting Query * * */
|
||||
|
||||
String boostQuery = params.get(DMP.BQ);
|
||||
if (null != boostQuery && !boostQuery.equals("")) {
|
||||
Query tmp = p.parse(boostQuery);
|
||||
/* if the default boost was used, and we've got a BooleanQuery
|
||||
* extract the subqueries out and use them directly
|
||||
*/
|
||||
if (1.0f == tmp.getBoost() && tmp instanceof BooleanQuery) {
|
||||
for (BooleanClause c : ((BooleanQuery)tmp).getClauses()) {
|
||||
query.add(c);
|
||||
String[] boostParams = params.getParams(DMP.BQ);
|
||||
List<Query> boostQueries = U.parseQueryStrings(req, boostParams);
|
||||
if (null != boostQueries) {
|
||||
if(1 == boostQueries.size() && 1 == boostParams.length) {
|
||||
/* legacy logic */
|
||||
Query f = boostQueries.get(0);
|
||||
if (1.0f == f.getBoost() && f instanceof BooleanQuery) {
|
||||
/* if the default boost was used, and we've got a BooleanQuery
|
||||
* extract the subqueries out and use them directly
|
||||
*/
|
||||
for (BooleanClause c : ((BooleanQuery)f).getClauses()) {
|
||||
query.add(c);
|
||||
}
|
||||
} else {
|
||||
query.add(f, BooleanClause.Occur.SHOULD);
|
||||
}
|
||||
} else {
|
||||
query.add(tmp, BooleanClause.Occur.SHOULD);
|
||||
for(Query f : boostQueries) {
|
||||
query.add(f, BooleanClause.Occur.SHOULD);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* * * Boosting Functions * * */
|
||||
|
||||
String boostFunc = params.get(DMP.BF);
|
||||
if (null != boostFunc && !boostFunc.equals("")) {
|
||||
List<Query> funcs = U.parseFuncs(schema, boostFunc);
|
||||
for (Query f : funcs) {
|
||||
query.add(f, Occur.SHOULD);
|
||||
String[] boostFuncs = params.getParams(DMP.BF);
|
||||
if (null != boostFuncs && 0 != boostFuncs.length) {
|
||||
for (String boostFunc : boostFuncs) {
|
||||
if(null == boostFunc || "".equals(boostFunc)) continue;
|
||||
List<Query> funcs = U.parseFuncs(schema, boostFunc);
|
||||
for (Query f : funcs) {
|
||||
query.add(f, Occur.SHOULD);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -318,22 +338,23 @@ public class DisMaxRequestHandler extends RequestHandlerBase {
|
|||
NamedList debug = U.doStandardDebug(req, userQuery, query, results.docList);
|
||||
if (null != debug) {
|
||||
debug.add("altquerystring", altUserQuery);
|
||||
debug.add("boostquery", boostQuery);
|
||||
debug.add("boostfunc", boostFunc);
|
||||
if (null != boostQueries) {
|
||||
debug.add("boost_queries", boostParams);
|
||||
debug.add("parsed_boost_queries",
|
||||
QueryParsing.toString(boostQueries, req.getSchema()));
|
||||
}
|
||||
debug.add("boostfuncs", params.getParams(DMP.BF));
|
||||
if (null != restrictions) {
|
||||
debug.add("filter_queries", params.getParams(FQ));
|
||||
List<String> fqs = new ArrayList<String>(restrictions.size());
|
||||
for (Query fq : restrictions) {
|
||||
fqs.add(QueryParsing.toString(fq, req.getSchema()));
|
||||
}
|
||||
debug.add("parsed_filter_queries",fqs);
|
||||
debug.add("parsed_filter_queries",
|
||||
QueryParsing.toString(restrictions, req.getSchema()));
|
||||
}
|
||||
rsp.add("debug", debug);
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
SolrException.logOnce(SolrCore.log,
|
||||
"Exception durring debug", e);
|
||||
"Exception during debug", e);
|
||||
rsp.add("exception_during_debug", SolrException.toStr(e));
|
||||
}
|
||||
|
||||
|
@ -341,8 +362,11 @@ public class DisMaxRequestHandler extends RequestHandlerBase {
|
|||
if(HighlightingUtils.isHighlightingEnabled(req) && parsedUserQuery != null) {
|
||||
String[] highFields = queryFields.keySet().toArray(new String[0]);
|
||||
NamedList sumData =
|
||||
HighlightingUtils.doHighlighting(results.docList, parsedUserQuery,
|
||||
req, highFields);
|
||||
HighlightingUtils.doHighlighting(
|
||||
results.docList,
|
||||
parsedUserQuery.rewrite(req.getSearcher().getReader()),
|
||||
req,
|
||||
highFields);
|
||||
if(sumData != null)
|
||||
rsp.add("highlighting", sumData);
|
||||
}
|
||||
|
|
|
@ -138,9 +138,9 @@ public class StandardRequestHandler extends RequestHandlerBase {
|
|||
SolrException.logOnce(SolrCore.log, "Exception during debug", e);
|
||||
rsp.add("exception_during_debug", SolrException.toStr(e));
|
||||
}
|
||||
|
||||
|
||||
NamedList sumData = HighlightingUtils.doHighlighting(
|
||||
results.docList, query, req, new String[]{defaultField});
|
||||
results.docList, query.rewrite(req.getSearcher().getReader()), req, new String[]{defaultField});
|
||||
if(sumData != null)
|
||||
rsp.add("highlighting", sumData);
|
||||
}
|
||||
|
|
|
@ -31,6 +31,7 @@ import org.apache.solr.schema.FieldType;
|
|||
import org.apache.solr.request.SolrParams;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.logging.Level;
|
||||
import java.io.IOException;
|
||||
|
@ -469,6 +470,16 @@ public class QueryParsing {
|
|||
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a list of String which are stringified versions of a list of Queries
|
||||
*/
|
||||
public static List<String> toString(List<Query> queries, IndexSchema schema) {
|
||||
List<String> out = new ArrayList<String>(queries.size());
|
||||
for (Query q : queries) {
|
||||
out.add(QueryParsing.toString(q, schema));
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
private static ValueSource parseValSource(StrParser sp, IndexSchema schema) throws ParseException {
|
||||
String id = sp.getId();
|
||||
|
|
|
@ -483,20 +483,31 @@ public class SolrPluginUtils {
|
|||
* @return Map of fieldOne => 2.3, fieldTwo => null, fieldThree => -0.4
|
||||
*/
|
||||
public static Map<String,Float> parseFieldBoosts(String in) {
|
||||
|
||||
if (null == in || "".equals(in.trim())) {
|
||||
return parseFieldBoosts(new String[]{in});
|
||||
}
|
||||
/**
|
||||
* Like <code>parseFieldBoosts(String)</code>, but parses all the strings
|
||||
* in the provided array (which may be null).
|
||||
*
|
||||
* @param fieldList an array of Strings eg. <code>{"fieldOne^2.3", "fieldTwo"}</code>
|
||||
* @return Map of fieldOne => 2.3, fieldThree => -0.4
|
||||
*/
|
||||
public static Map<String,Float> parseFieldBoosts(String[] fieldLists) {
|
||||
if (null == fieldLists || 0 == fieldLists.length) {
|
||||
return new HashMap<String,Float>();
|
||||
}
|
||||
|
||||
String[] bb = in.trim().split("\\s+");
|
||||
Map<String, Float> out = new HashMap<String,Float>(7);
|
||||
for (String s : bb) {
|
||||
String[] bbb = s.split("\\^");
|
||||
out.put(bbb[0], 1 == bbb.length ? null : Float.valueOf(bbb[1]));
|
||||
for (String in : fieldLists) {
|
||||
if (null == in || "".equals(in.trim()))
|
||||
continue;
|
||||
String[] bb = in.trim().split("\\s+");
|
||||
for (String s : bb) {
|
||||
String[] bbb = s.split("\\^");
|
||||
out.put(bbb[0], 1 == bbb.length ? null : Float.valueOf(bbb[1]));
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a string containing functions with optional boosts, returns
|
||||
* an array of Queries representing those functions with the specified
|
||||
|
@ -804,10 +815,16 @@ public class SolrPluginUtils {
|
|||
* @return null if no filter queries
|
||||
*/
|
||||
public static List<Query> parseFilterQueries(SolrQueryRequest req) throws ParseException {
|
||||
String[] in = req.getParams().getParams(SolrParams.FQ);
|
||||
|
||||
if (null == in || 0 == in.length) return null;
|
||||
return parseQueryStrings(req, req.getParams().getParams(SolrParams.FQ));
|
||||
}
|
||||
|
||||
/** Turns an array of query strings into a List of Query objects.
|
||||
*
|
||||
* @return null if no queries are generated
|
||||
*/
|
||||
public static List<Query> parseQueryStrings(SolrQueryRequest req,
|
||||
String[] queries) throws ParseException {
|
||||
if (null == queries || 0 == queries.length) return null;
|
||||
List<Query> out = new LinkedList<Query>();
|
||||
SolrIndexSearcher s = req.getSearcher();
|
||||
/* Ignore SolrParams.DF - could have init param FQs assuming the
|
||||
|
@ -815,7 +832,7 @@ public class SolrPluginUtils {
|
|||
* If user doesn't want schema default, they should be explicit in the FQ.
|
||||
*/
|
||||
SolrQueryParser qp = new SolrQueryParser(s.getSchema(), null);
|
||||
for (String q : in) {
|
||||
for (String q : queries) {
|
||||
if (null != q && 0 != q.trim().length()) {
|
||||
out.add(qp.parse(q));
|
||||
}
|
||||
|
|
|
@ -29,6 +29,7 @@ import java.io.ByteArrayInputStream;
|
|||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.Map;
|
||||
import java.util.HashMap;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Tests some basic functionality of the DisMaxRequestHandler
|
||||
|
@ -46,8 +47,8 @@ public class DisMaxRequestHandlerTest extends AbstractSolrTestCase {
|
|||
"facet.field","t_s"
|
||||
);
|
||||
}
|
||||
public void testSomeStuff() throws Exception {
|
||||
|
||||
/** Add some documents to the index */
|
||||
protected void populate() {
|
||||
assertU(adoc("id", "666",
|
||||
"features_t", "cool and scary stuff",
|
||||
"subject", "traveling in hell",
|
||||
|
@ -77,7 +78,11 @@ public class DisMaxRequestHandlerTest extends AbstractSolrTestCase {
|
|||
"weight", "97.3",
|
||||
"iind", "8675309"));
|
||||
assertU(commit());
|
||||
|
||||
}
|
||||
|
||||
public void testSomeStuff() throws Exception {
|
||||
populate();
|
||||
|
||||
assertQ("basic match",
|
||||
req("guide")
|
||||
,"//*[@numFound='2']"
|
||||
|
@ -94,6 +99,42 @@ public class DisMaxRequestHandlerTest extends AbstractSolrTestCase {
|
|||
,"//result/doc[2]/int[@name='id'][.='666']"
|
||||
,"//result/doc[3]/int[@name='id'][.='8675309']"
|
||||
);
|
||||
|
||||
assertQ("multi qf",
|
||||
req("q", "cool"
|
||||
,"qt", "dismax"
|
||||
,"version", "2.0"
|
||||
,"qf", "subject"
|
||||
,"qf", "features_t"
|
||||
)
|
||||
,"//*[@numFound='3']"
|
||||
);
|
||||
|
||||
assertQ("boost query",
|
||||
req("q", "cool stuff"
|
||||
,"qt", "dismax"
|
||||
,"version", "2.0"
|
||||
,"bq", "subject:hell^400"
|
||||
)
|
||||
,"//*[@numFound='3']"
|
||||
,"//result/doc[1]/int[@name='id'][.='666']"
|
||||
,"//result/doc[2]/int[@name='id'][.='42']"
|
||||
,"//result/doc[3]/int[@name='id'][.='8675309']"
|
||||
);
|
||||
|
||||
assertQ("multi boost query",
|
||||
req("q", "cool stuff"
|
||||
,"qt", "dismax"
|
||||
,"version", "2.0"
|
||||
,"bq", "subject:hell^400"
|
||||
,"bq", "subject:cool^4"
|
||||
,"debugQuery", "true"
|
||||
)
|
||||
,"//*[@numFound='3']"
|
||||
,"//result/doc[1]/int[@name='id'][.='666']"
|
||||
,"//result/doc[2]/int[@name='id'][.='8675309']"
|
||||
,"//result/doc[3]/int[@name='id'][.='42']"
|
||||
);
|
||||
|
||||
assertQ("minimum mm is three",
|
||||
req("cool stuff traveling")
|
||||
|
@ -135,6 +176,33 @@ public class DisMaxRequestHandlerTest extends AbstractSolrTestCase {
|
|||
);
|
||||
}
|
||||
|
||||
public void testExtraBlankBQ() throws Exception {
|
||||
populate();
|
||||
// if the boost queries are in their own boolean query, the clauses will be
|
||||
// surrounded by ()'s in the debug output
|
||||
Pattern p = Pattern.compile("subject:hell\\s*subject:cool");
|
||||
Pattern p_bool = Pattern.compile("\\(subject:hell\\s*subject:cool\\)");
|
||||
String resp = h.query(req("q", "cool stuff"
|
||||
,"qt", "dismax"
|
||||
,"version", "2.0"
|
||||
,"bq", "subject:hell OR subject:cool"
|
||||
,"debugQuery", "true"
|
||||
));
|
||||
assertTrue(p.matcher(resp).find());
|
||||
assertFalse(p_bool.matcher(resp).find());
|
||||
|
||||
resp = h.query(req("q", "cool stuff"
|
||||
,"qt", "dismax"
|
||||
,"version", "2.0"
|
||||
,"bq", "subject:hell OR subject:cool"
|
||||
,"bq",""
|
||||
,"debugQuery", "true"
|
||||
));
|
||||
assertTrue(p.matcher(resp).find());
|
||||
assertTrue(p_bool.matcher(resp).find());
|
||||
|
||||
}
|
||||
|
||||
public void testOldStyleDefaults() throws Exception {
|
||||
|
||||
lrf = h.getRequestFactory
|
||||
|
|
|
@ -92,6 +92,10 @@ public class SolrPluginUtilsTest extends AbstractSolrTestCase {
|
|||
(" fieldOne^2.3 fieldTwo fieldThree^-0.4 "));
|
||||
assertEquals("really spacey e1", e1, SolrPluginUtils.parseFieldBoosts
|
||||
(" \t fieldOne^2.3 \n fieldTwo fieldThree^-0.4 "));
|
||||
assertEquals("really spacey e1", e1, SolrPluginUtils.parseFieldBoosts
|
||||
(new String[]{" \t fieldOne^2.3 \n",
|
||||
" fieldTwo fieldThree^-0.4 ",
|
||||
" "}));
|
||||
|
||||
Map<String,Float> e2 = new HashMap<String,Float>();
|
||||
assertEquals("empty e2", e2, SolrPluginUtils.parseFieldBoosts
|
||||
|
|
Loading…
Reference in New Issue