mirror of https://github.com/apache/lucene.git
SOLR-4481: SwitchQParserPlugin registered by default as 'switch'
git-svn-id: https://svn.apache.org/repos/asf/lucene/dev/trunk@1449809 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
1115ba8aba
commit
01b504b137
|
@ -84,6 +84,10 @@ New Features
|
|||
* SOLR-4498: Add list command to ZkCLI that prints out the contents of
|
||||
ZooKeeper. (Roman Shaposhnik via Mark Miller)
|
||||
|
||||
* SOLR-4481: SwitchQParserPlugin registered by default as 'switch' using
|
||||
syntax: {!switch case=XXX case.foo=YYY case.bar=ZZZ default=QQQ}foo
|
||||
(hossman)
|
||||
|
||||
Bug Fixes
|
||||
----------------------
|
||||
|
||||
|
|
|
@ -42,6 +42,7 @@ public abstract class QParserPlugin implements NamedListInitializedPlugin {
|
|||
SpatialBoxQParserPlugin.NAME, SpatialBoxQParserPlugin.class,
|
||||
JoinQParserPlugin.NAME, JoinQParserPlugin.class,
|
||||
SurroundQParserPlugin.NAME, SurroundQParserPlugin.class,
|
||||
SwitchQParserPlugin.NAME, SwitchQParserPlugin.class,
|
||||
};
|
||||
|
||||
/** return a {@link QParser} */
|
||||
|
|
|
@ -0,0 +1,147 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.solr.search;
|
||||
|
||||
import org.apache.lucene.search.Query;
|
||||
import org.apache.solr.common.params.SolrParams;
|
||||
import org.apache.solr.common.util.NamedList;
|
||||
import org.apache.solr.request.SolrQueryRequest;
|
||||
import org.apache.solr.handler.component.SearchHandler; // jdoc
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
|
||||
/**
|
||||
* <p>A QParserPlugin that acts like a "switch/case" statement.</p>
|
||||
*
|
||||
* <p>
|
||||
* QParser's produced by this plugin will take their primary input string,
|
||||
* trimmed and prefixed with "<code>case.</code>", to use as a key to lookup a
|
||||
* "switch case" in the parser's local params. If a matching local param is
|
||||
* found the resulting param value will then be parsed as a subquery, and
|
||||
* returned as the parse result.
|
||||
* </p>
|
||||
* <p>
|
||||
* The "<code>case</code>" local param can be optionally be specified as a
|
||||
* switch case to match missing (or blank) input strings.
|
||||
* The "<code>default</code>" local param can optionally be specified
|
||||
* as a default case to use if the input string does not match any other
|
||||
* switch case local params. If <code>default</code> is not specified,
|
||||
* then any input which does not match a switch case local param will result
|
||||
* in a syntax error.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* In the examples below, the result of each query would be <code>XXX</code>....
|
||||
* </p>
|
||||
* <pre>
|
||||
* q={!switch case.foo=XXX case.bar=zzz case.yak=qqq}foo
|
||||
* q={!switch case.foo=qqq case.bar=XXX case.yak=zzz} bar // extra whitespace
|
||||
* q={!switch case.foo=qqq case.bar=zzz default=XXX}asdf // fallback on default
|
||||
* q={!switch case=XXX case.bar=zzz case.yak=qqq} // blank input
|
||||
* </pre>
|
||||
*
|
||||
* <p>
|
||||
* A practical usage of this QParsePlugin, is in specifying "appends"
|
||||
* <code>fq</code> params in the configuration of a {@link SearchHandler}, to
|
||||
* provide a fixed set of filter options for clients using custom parameter
|
||||
* names.
|
||||
* Using the example configuration below, clients can optionally specify the
|
||||
* custom parameters <code>in_stock</code> and <code>shipping</code> to
|
||||
* override the default filtering behavior, but are limited to the specific
|
||||
* set of legal values (<code>shipping=any|free</code>,
|
||||
* <code>in_stock=yes|no|all</code>).
|
||||
* </p>
|
||||
*
|
||||
* <pre class="prettyprint">
|
||||
* <requestHandler name="/select" class="solr.SearchHandler">
|
||||
* <lst name="defaults">
|
||||
* <str name="in_stock">yes</str>
|
||||
* <str name="shipping">any</str>
|
||||
* </lst>
|
||||
* <lst name="appends">
|
||||
* <str name="fq">{!switch case.all='*:*'
|
||||
* case.yes='inStock:true'
|
||||
* case.no='inStock:false'
|
||||
* v=$in_stock}</str>
|
||||
* <str name="fq">{!switch case.any='*:*'
|
||||
* case.free='shipping_cost:0.0'
|
||||
* v=$shipping}</str>
|
||||
* </lst>
|
||||
* </requestHandler></pre>
|
||||
*/
|
||||
public class SwitchQParserPlugin extends QParserPlugin {
|
||||
public static String NAME = "switch";
|
||||
|
||||
/**
|
||||
* Used as both a local params key to find the "default" if no
|
||||
* blank input is provided to the parser, as well as a prefix (followed by
|
||||
* '.' for looking up the switch input.
|
||||
*/
|
||||
public static String SWITCH_CASE = "case";
|
||||
|
||||
/**
|
||||
* A local param whose value, if specified, is used if no switch case
|
||||
* matches the parser input. If this param is not specified, and no
|
||||
* switch case matches the parser input, an error is returned.
|
||||
*/
|
||||
public static String SWITCH_DEFAULT = "default";
|
||||
|
||||
@Override
|
||||
public void init(NamedList args) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public QParser createParser(String qstr, SolrParams localParams, SolrParams params, SolrQueryRequest req) {
|
||||
return new QParser(qstr, localParams, params, req) {
|
||||
QParser subParser;
|
||||
|
||||
@Override
|
||||
public Query parse() throws SyntaxError {
|
||||
String val = localParams.get(QueryParsing.V);
|
||||
|
||||
// we don't want to wrapDefaults arround params, because then
|
||||
// clients could add their own switch options
|
||||
String subQ = localParams.get(SWITCH_DEFAULT);
|
||||
subQ = StringUtils.isBlank(val)
|
||||
? localParams.get(SWITCH_CASE, subQ)
|
||||
: localParams.get(SWITCH_CASE + "." + val.trim(), subQ);
|
||||
|
||||
if (null == subQ) {
|
||||
throw new SyntaxError("No "+SWITCH_DEFAULT+", and no switch case matching specified query string: \"" + val + "\"");
|
||||
}
|
||||
|
||||
subParser = subQuery(subQ, null);
|
||||
return subParser.getQuery();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getDefaultHighlightFields() {
|
||||
return subParser.getDefaultHighlightFields();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Query getHighlightQuery() throws SyntaxError {
|
||||
return subParser.getHighlightQuery();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addDebugInfo(NamedList<Object> debugInfo) {
|
||||
subParser.addDebugInfo(debugInfo);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
|
@ -113,6 +113,31 @@ public class QueryEqualityTest extends SolrTestCaseJ4 {
|
|||
}
|
||||
}
|
||||
|
||||
public void testQuerySwitch() throws Exception {
|
||||
SolrQueryRequest req = req("myXXX", "XXX",
|
||||
"myField", "foo_s",
|
||||
"myQ", "{!prefix f=$myField}asdf");
|
||||
try {
|
||||
assertQueryEquals("switch", req,
|
||||
"{!switch case.foo=XXX case.bar=zzz case.yak=qqq}foo",
|
||||
"{!switch case.foo=qqq case.bar=XXX case.yak=zzz} bar ",
|
||||
"{!switch case.foo=qqq case.bar=XXX case.yak=zzz v=' bar '}",
|
||||
"{!switch default=XXX case.foo=qqq case.bar=zzz}asdf",
|
||||
"{!switch default=$myXXX case.foo=qqq case.bar=zzz}asdf",
|
||||
"{!switch case=XXX case.bar=zzz case.yak=qqq v=''}",
|
||||
"{!switch case.bar=zzz case=XXX case.yak=qqq v=''}",
|
||||
"{!switch case=XXX case.bar=zzz case.yak=qqq}",
|
||||
"{!switch case=XXX case.bar=zzz case.yak=qqq} ",
|
||||
"{!switch case=$myXXX case.bar=zzz case.yak=qqq} ");
|
||||
|
||||
assertQueryEquals("switch", req,
|
||||
"{!switch case.foo=$myQ case.bar=zzz case.yak=qqq}foo",
|
||||
"{!query v=$myQ}");
|
||||
} finally {
|
||||
req.close();
|
||||
}
|
||||
}
|
||||
|
||||
public void testQueryDismax() throws Exception {
|
||||
for (final String type : new String[]{"dismax","edismax"}) {
|
||||
assertQueryEquals(type, "{!"+type+"}apache solr",
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
*/
|
||||
package org.apache.solr.search;
|
||||
|
||||
import org.apache.solr.common.SolrException;
|
||||
import org.apache.solr.common.params.CommonParams;
|
||||
import org.apache.solr.util.AbstractSolrTestCase;
|
||||
import org.junit.BeforeClass;
|
||||
|
@ -336,6 +337,63 @@ public class TestQueryTypes extends AbstractSolrTestCase {
|
|||
,"//doc[./float[@name='v_f']='1.5' and ./float[@name='score']='2.25']"
|
||||
);
|
||||
|
||||
// switch queries
|
||||
assertQ("test matching switch query",
|
||||
req("df", "v_t",
|
||||
"q", "{!switch case.x=Dude case.z=Yonik} x ")
|
||||
,"//result[@numFound='1']"
|
||||
,"//*[@name='id'][.='1.0']");
|
||||
assertQ("test empty matching switch query",
|
||||
req("df", "v_t",
|
||||
"q", "{!switch case.x=Dude case=Yonik} ")
|
||||
,"//result[@numFound='1']"
|
||||
,"//*[@name='id'][.='2.0']");
|
||||
assertQ("test empty matching switch query",
|
||||
req("df", "v_t",
|
||||
"q", "{!switch case.x=Dude case=Yonik v=''}")
|
||||
,"//result[@numFound='1']"
|
||||
,"//*[@name='id'][.='2.0']");
|
||||
assertQ("test empty matching switch query",
|
||||
req("df", "v_t",
|
||||
"q", "{!switch case.x=Dude case=Yonik v=$qq}")
|
||||
,"//result[@numFound='1']"
|
||||
,"//*[@name='id'][.='2.0']");
|
||||
assertQ("test matching switch query w/deref",
|
||||
req("q", "{!switch case.x=$d case.z=Yonik} x ",
|
||||
"df", "v_t",
|
||||
"d", "Dude")
|
||||
,"//result[@numFound='1']"
|
||||
,"//*[@name='id'][.='1.0']");
|
||||
assertQ("test default switch query",
|
||||
req("q", "{!switch default=$d case.x=$d case.z=Yonik}asdf",
|
||||
"df", "v_t",
|
||||
"d", "Dude")
|
||||
,"//result[@numFound='1']"
|
||||
,"//*[@name='id'][.='1.0']");
|
||||
assertQ("test empty default switch query",
|
||||
req("q", "{!switch default=$d case.x=$d case.z=Yonik v=$qq}",
|
||||
"df", "v_t",
|
||||
"d", "Dude")
|
||||
,"//result[@numFound='1']"
|
||||
,"//*[@name='id'][.='1.0']");
|
||||
|
||||
try {
|
||||
ignoreException("No\\ default\\, and no switch case");
|
||||
assertQ("no match and no default",
|
||||
req("q", "{!switch case.x=Dude case.z=Yonik}asdf")
|
||||
,"//result[@numFound='BOGUS']");
|
||||
fail("Should have gotten an error w/o default");
|
||||
} catch (RuntimeException exp) {
|
||||
assertTrue("exp cause is wrong",
|
||||
exp.getCause() instanceof SolrException);
|
||||
SolrException e = (SolrException) exp.getCause();
|
||||
assertEquals("error isn't user error", 400, e.code());
|
||||
assertTrue("Error doesn't include bad switch case: " + e.getMessage(),
|
||||
e.getMessage().contains("asdf"));
|
||||
} finally {
|
||||
resetExceptionIgnores();
|
||||
}
|
||||
|
||||
|
||||
// dismax query from std request handler
|
||||
assertQ("test dismax query",
|
||||
|
|
Loading…
Reference in New Issue