You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by ho...@apache.org on 2013/02/25 19:54:40 UTC
svn commit: r1449809 - in /lucene/dev/trunk/solr: ./
core/src/java/org/apache/solr/search/ core/src/test/org/apache/solr/search/
Author: hossman
Date: Mon Feb 25 18:54:39 2013
New Revision: 1449809
URL: http://svn.apache.org/r1449809
Log:
SOLR-4481: SwitchQParserPlugin registered by default as 'switch'
Added:
lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/SwitchQParserPlugin.java (with props)
Modified:
lucene/dev/trunk/solr/CHANGES.txt
lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/QParserPlugin.java
lucene/dev/trunk/solr/core/src/test/org/apache/solr/search/QueryEqualityTest.java
lucene/dev/trunk/solr/core/src/test/org/apache/solr/search/TestQueryTypes.java
Modified: lucene/dev/trunk/solr/CHANGES.txt
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/CHANGES.txt?rev=1449809&r1=1449808&r2=1449809&view=diff
==============================================================================
--- lucene/dev/trunk/solr/CHANGES.txt (original)
+++ lucene/dev/trunk/solr/CHANGES.txt Mon Feb 25 18:54:39 2013
@@ -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
----------------------
Modified: lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/QParserPlugin.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/QParserPlugin.java?rev=1449809&r1=1449808&r2=1449809&view=diff
==============================================================================
--- lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/QParserPlugin.java (original)
+++ lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/QParserPlugin.java Mon Feb 25 18:54:39 2013
@@ -42,6 +42,7 @@ public abstract class QParserPlugin impl
SpatialBoxQParserPlugin.NAME, SpatialBoxQParserPlugin.class,
JoinQParserPlugin.NAME, JoinQParserPlugin.class,
SurroundQParserPlugin.NAME, SurroundQParserPlugin.class,
+ SwitchQParserPlugin.NAME, SwitchQParserPlugin.class,
};
/** return a {@link QParser} */
Added: lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/SwitchQParserPlugin.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/SwitchQParserPlugin.java?rev=1449809&view=auto
==============================================================================
--- lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/SwitchQParserPlugin.java (added)
+++ lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/SwitchQParserPlugin.java Mon Feb 25 18:54:39 2013
@@ -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);
+ }
+ };
+ }
+}
Modified: lucene/dev/trunk/solr/core/src/test/org/apache/solr/search/QueryEqualityTest.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/core/src/test/org/apache/solr/search/QueryEqualityTest.java?rev=1449809&r1=1449808&r2=1449809&view=diff
==============================================================================
--- lucene/dev/trunk/solr/core/src/test/org/apache/solr/search/QueryEqualityTest.java (original)
+++ lucene/dev/trunk/solr/core/src/test/org/apache/solr/search/QueryEqualityTest.java Mon Feb 25 18:54:39 2013
@@ -113,6 +113,31 @@ public class QueryEqualityTest extends S
}
}
+ 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",
Modified: lucene/dev/trunk/solr/core/src/test/org/apache/solr/search/TestQueryTypes.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/core/src/test/org/apache/solr/search/TestQueryTypes.java?rev=1449809&r1=1449808&r2=1449809&view=diff
==============================================================================
--- lucene/dev/trunk/solr/core/src/test/org/apache/solr/search/TestQueryTypes.java (original)
+++ lucene/dev/trunk/solr/core/src/test/org/apache/solr/search/TestQueryTypes.java Mon Feb 25 18:54:39 2013
@@ -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 Abst
,"//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",