You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by sh...@apache.org on 2013/11/16 10:16:57 UTC

svn commit: r1542486 - in /lucene/dev/trunk/solr: ./ core/src/java/org/apache/solr/search/ core/src/test-files/solr/collection1/conf/ core/src/test/org/apache/solr/search/ solrj/src/java/org/apache/solr/common/params/

Author: shalin
Date: Sat Nov 16 09:16:57 2013
New Revision: 1542486

URL: http://svn.apache.org/r1542486
Log:
SOLR-5447: Add a QParserPlugin for Lucene's SimpleQueryParser

Added:
    lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/SimpleQParserPlugin.java   (with props)
    lucene/dev/trunk/solr/core/src/test-files/solr/collection1/conf/schema-simpleqpplugin.xml   (with props)
    lucene/dev/trunk/solr/core/src/test/org/apache/solr/search/TestSimpleQParserPlugin.java   (with props)
    lucene/dev/trunk/solr/solrj/src/java/org/apache/solr/common/params/SimpleParams.java   (with props)
Modified:
    lucene/dev/trunk/solr/CHANGES.txt
    lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/QParserPlugin.java

Modified: lucene/dev/trunk/solr/CHANGES.txt
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/CHANGES.txt?rev=1542486&r1=1542485&r2=1542486&view=diff
==============================================================================
--- lucene/dev/trunk/solr/CHANGES.txt (original)
+++ lucene/dev/trunk/solr/CHANGES.txt Sat Nov 16 09:16:57 2013
@@ -84,6 +84,9 @@ New Features
 * SOLR-5287: You can edit files in the conf directory from the admin UI 
   (Erick Erickson, Stefan Matheis)
 
+* SOLR-5447: Add a QParserPlugin for Lucene's SimpleQueryParser.
+  (Jack Conradson via shalin)
+
 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=1542486&r1=1542485&r2=1542486&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 Sat Nov 16 09:16:57 2013
@@ -52,7 +52,8 @@ public abstract class QParserPlugin impl
     MaxScoreQParserPlugin.NAME, MaxScoreQParserPlugin.class,
     BlockJoinParentQParserPlugin.NAME, BlockJoinParentQParserPlugin.class,
     BlockJoinChildQParserPlugin.NAME, BlockJoinChildQParserPlugin.class,
-    CollapsingQParserPlugin.NAME, CollapsingQParserPlugin.class
+    CollapsingQParserPlugin.NAME, CollapsingQParserPlugin.class,
+    SimpleQParserPlugin.NAME, SimpleQParserPlugin.class
   };
 
   /** return a {@link QParser} */

Added: lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/SimpleQParserPlugin.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/SimpleQParserPlugin.java?rev=1542486&view=auto
==============================================================================
--- lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/SimpleQParserPlugin.java (added)
+++ lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/SimpleQParserPlugin.java Sat Nov 16 09:16:57 2013
@@ -0,0 +1,173 @@
+package org.apache.solr.search;
+
+/*
+ * 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.lucene.queryparser.simple.SimpleQueryParser;
+import org.apache.lucene.search.BooleanClause;
+import org.apache.lucene.search.Query;
+import org.apache.solr.common.params.CommonParams;
+import org.apache.solr.common.params.SimpleParams;
+import org.apache.solr.common.params.SolrParams;
+import org.apache.solr.common.util.NamedList;
+import org.apache.solr.parser.QueryParser;
+import org.apache.solr.request.SolrQueryRequest;
+import org.apache.solr.util.SolrPluginUtils;
+
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+
+/**
+ * Create a query from the input value that will be parsed by Lucene's SimpleQueryParser.
+ * See {@link org.apache.lucene.queryparser.simple.SimpleQueryParser} for details on the exact syntax allowed
+ * to be used for queries.
+ * <br>
+ * The following options may be applied for parsing the query.
+ * <ul>
+ *   <li>
+ *     q.operations - Used to enable specific operations for parsing.  The operations that can be enabled are
+ *                    and, not, or, prefix, phrase, precedence, escape, and whitespace.  By default all operations
+ *                    are enabled.  All operations can be disabled by passing in an empty string to this parameter.
+ *   </li>
+ *   <li>
+ *     q.op - Used to specify the operator to be used if whitespace is a delimiter. Either 'AND' or 'OR'
+ *            can be specified for this parameter.  Any other string will cause an exception to be thrown.
+ *            If this parameter is not specified 'OR' will be used by default.
+ *   </li>
+ *   <li>
+ *     qf - The list of query fields and boosts to use when building the simple query.  The format is the following:
+ *          <code>fieldA^1.0 fieldB^2.2</code>.  A field can also be specified without a boost by simply listing the
+ *          field as <code>fieldA fieldB</code>.  Any field without a boost will default to use a boost of 1.0.
+ *   </li>
+ *   <li>
+ *     df - An override for the default field specified in the schema or a default field if one is not specified
+ *          in the schema.  If qf is not specified the default field will be used as the field to run the query
+ *          against.
+ *   </li>
+ * </ul>
+ */
+public class SimpleQParserPlugin extends QParserPlugin {
+  /** The name that can be used to specify this plugin should be used to parse the query. */
+  public static String NAME = "simple";
+
+  /** Enables {@code AND} operator (+) */
+  private static final String AND_OPERATOR         = "AND";
+  /** Enables {@code NOT} operator (-) */
+  private static final String NOT_OPERATOR         = "NOT";
+  /** Enables {@code OR} operator (|) */
+  private static final String OR_OPERATOR          = "OR";
+  /** Enables {@code PREFIX} operator (*) */
+  private static final String PREFIX_OPERATOR      = "PREFIX";
+  /** Enables {@code PHRASE} operator (") */
+  private static final String PHRASE_OPERATOR      = "PHRASE";
+  /** Enables {@code PRECEDENCE} operators: {@code (} and {@code )} */
+  private static final String PRECEDENCE_OPERATORS = "PRECEDENCE";
+  /** Enables {@code ESCAPE} operator (\) */
+  private static final String ESCAPE_OPERATOR      = "ESCAPE";
+  /** Enables {@code WHITESPACE} operators: ' ' '\n' '\r' '\t' */
+  private static final String WHITESPACE_OPERATOR  = "WHITESPACE";
+
+  /** Map of string operators to their int counterparts in SimpleQueryParser. */
+  private static final Map<String, Integer> OPERATORS = new HashMap<String, Integer>();
+
+  /* Setup the map of possible operators. */
+  static {
+    OPERATORS.put(AND_OPERATOR,         SimpleQueryParser.AND_OPERATOR);
+    OPERATORS.put(NOT_OPERATOR,         SimpleQueryParser.NOT_OPERATOR);
+    OPERATORS.put(OR_OPERATOR,          SimpleQueryParser.OR_OPERATOR);
+    OPERATORS.put(PREFIX_OPERATOR,      SimpleQueryParser.PREFIX_OPERATOR);
+    OPERATORS.put(PHRASE_OPERATOR,      SimpleQueryParser.PHRASE_OPERATOR);
+    OPERATORS.put(PRECEDENCE_OPERATORS, SimpleQueryParser.PRECEDENCE_OPERATORS);
+    OPERATORS.put(ESCAPE_OPERATOR,      SimpleQueryParser.ESCAPE_OPERATOR);
+    OPERATORS.put(WHITESPACE_OPERATOR,  SimpleQueryParser.WHITESPACE_OPERATOR);
+  }
+
+  /** No initialization is necessary so this method is empty. */
+  @Override
+  public void init(NamedList args) {
+  }
+
+  /** Returns a QParser that will create a query by using Lucene's SimpleQueryParser. */
+  @Override
+  public QParser createParser(String qstr, SolrParams localParams, SolrParams params, SolrQueryRequest req) {
+    // Some of the parameters may come in through localParams, so combine them with params.
+    SolrParams defaultParams = SolrParams.wrapDefaults(localParams, params);
+
+    // This will be used to specify what fields and boosts will be used by SimpleQueryParser.
+    Map<String, Float> queryFields = SolrPluginUtils.parseFieldBoosts(defaultParams.get(SimpleParams.QF));
+
+    if (queryFields.isEmpty()) {
+      // It qf is not specified setup up the queryFields map to use the defaultField.
+      String defaultField = QueryParsing.getDefaultField(req.getSchema(), defaultParams.get(CommonParams.DF));
+
+      if (defaultField == null) {
+        // A query cannot be run without having a field or set of fields to run against.
+        throw new IllegalStateException("Neither " + SimpleParams.QF + ", " + CommonParams.DF
+            + ", nor the default search field are present.");
+      }
+
+      queryFields.put(defaultField, 1.0F);
+    }
+    else {
+      for (Map.Entry<String, Float> queryField : queryFields.entrySet()) {
+        if (queryField.getValue() == null) {
+          // Some fields may be specified without a boost, so default the boost to 1.0 since a null value
+          // will not be accepted by SimpleQueryParser.
+          queryField.setValue(1.0F);
+        }
+      }
+    }
+
+    // Setup the operations that are enabled for the query.
+    int enabledOps = 0;
+    String opParam = defaultParams.get(SimpleParams.QO);
+
+    if (opParam == null) {
+      // All operations will be enabled.
+      enabledOps = -1;
+    } else {
+      // Parse the specified enabled operations to be used by the query.
+      String[] operations = opParam.split(",");
+
+      for (String operation : operations) {
+        Integer enabledOp = OPERATORS.get(operation.trim().toUpperCase(Locale.getDefault()));
+
+        if (enabledOp != null) {
+          enabledOps |= enabledOp;
+        }
+      }
+    }
+
+    // Create a SimpleQueryParser using the analyzer from the schema.
+    final SimpleQueryParser parser = new SimpleQueryParser(req.getSchema().getAnalyzer(), queryFields, enabledOps);
+
+    // Set the default operator to be either 'AND' or 'OR' for the query.
+    QueryParser.Operator defaultOp = QueryParsing.getQueryParserDefaultOperator(req.getSchema(), defaultParams.get(QueryParsing.OP));
+
+    if (defaultOp == QueryParser.Operator.AND) {
+      parser.setDefaultOperator(BooleanClause.Occur.MUST);
+    }
+
+    // Return a QParser that wraps a SimpleQueryParser.
+    return new QParser(qstr, localParams, params, req) {
+      public Query parse() throws SyntaxError {
+        return parser.parse(qstr);
+      }
+    };
+  }
+}

Added: lucene/dev/trunk/solr/core/src/test-files/solr/collection1/conf/schema-simpleqpplugin.xml
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/core/src/test-files/solr/collection1/conf/schema-simpleqpplugin.xml?rev=1542486&view=auto
==============================================================================
--- lucene/dev/trunk/solr/core/src/test-files/solr/collection1/conf/schema-simpleqpplugin.xml (added)
+++ lucene/dev/trunk/solr/core/src/test-files/solr/collection1/conf/schema-simpleqpplugin.xml Sat Nov 16 09:16:57 2013
@@ -0,0 +1,51 @@
+<?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.
+-->
+
+<!-- Test schema file for SimpleQParserPlugin. -->
+
+<schema name="simpleqp" version="1.0">
+  <types>
+    <fieldType name="int" class="solr.TrieIntField" precisionStep="0" omitNorms="true" positionIncrementGap="0"/>
+
+    <!-- basic text field: no offsets -->
+    <fieldtype name="text" class="solr.TextField">
+      <analyzer>
+        <tokenizer class="solr.MockTokenizerFactory" pattern="whitespace"/>
+        <filter class="solr.LowerCaseFilterFactory"/>
+      </analyzer>
+    </fieldtype>
+
+    <!-- basic text field: except it will use the keyword analyzer -->
+    <fieldtype name="text-keyword" class="solr.TextField">
+      <analyzer>
+        <tokenizer class="solr.MockTokenizerFactory" pattern="keyword"/>
+      </analyzer>
+    </fieldtype>
+  </types>
+
+  <fields>
+    <field name="id" type="int" indexed="true" stored="true" multiValued="false" required="false"/>
+    <field name="text0" type="text" indexed="true" stored="true"/>
+    <field name="text1" type="text" indexed="true" stored="true"/>
+    <field name="text-keyword0" type="text-keyword" indexed="true" stored="true"/>
+  </fields>
+
+  <defaultSearchField>text0</defaultSearchField>
+  <uniqueKey>id</uniqueKey>
+</schema>

Added: lucene/dev/trunk/solr/core/src/test/org/apache/solr/search/TestSimpleQParserPlugin.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/core/src/test/org/apache/solr/search/TestSimpleQParserPlugin.java?rev=1542486&view=auto
==============================================================================
--- lucene/dev/trunk/solr/core/src/test/org/apache/solr/search/TestSimpleQParserPlugin.java (added)
+++ lucene/dev/trunk/solr/core/src/test/org/apache/solr/search/TestSimpleQParserPlugin.java Sat Nov 16 09:16:57 2013
@@ -0,0 +1,208 @@
+package org.apache.solr.search;
+
+/*
+ * 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.junit.BeforeClass;
+import org.junit.Test;
+
+/** Simple tests for SimpleQParserPlugin. */
+public class TestSimpleQParserPlugin extends SolrTestCaseJ4 {
+  @BeforeClass
+  public static void beforeClass() throws Exception {
+    initCore("solrconfig-basic.xml","schema-simpleqpplugin.xml");
+    index();
+  }
+
+  public static void index() throws Exception {
+    assertU(adoc("id", "42", "text0", "t0 t0 t0", "text1", "t0 t1 t2", "text-keyword0", "kw0 kw0 kw0"));
+    assertU(adoc("id", "43", "text0", "t0 t1 t2", "text1", "t3 t4 t5", "text-keyword0", "kw0 kw1 kw2"));
+    assertU(adoc("id", "44", "text0", "t0 t1 t1", "text1", "t6 t7 t8", "text-keyword0", "kw3 kw4 kw5"));
+    assertU(adoc("id", "45", "text0", "t0 t0 t1", "text1", "t9 t10 t11", "text-keyword0", "kw6 kw7 kw8"));
+    assertU(adoc("id", "46", "text0", "t1 t1 t1", "text1", "t12 t13 t14", "text-keyword0", "kw9 kw10 kw11"));
+    assertU(adoc("id", "47", "text0", "and", "text1", "+", "text-keyword0", "+"));
+    assertU(adoc("id", "48", "text0", "not", "text1", "-", "text-keyword0", "-"));
+    assertU(adoc("id", "49", "text0", "or", "text1", "|", "text-keyword0", "|"));
+    assertU(adoc("id", "50", "text0", "prefix", "text1", "t*", "text-keyword0", "kw*"));
+    assertU(adoc("id", "51", "text0", "phrase", "text1", "\"", "text-keyword0", "\""));
+    assertU(adoc("id", "52", "text0", "open", "text1", "(", "text-keyword0", "("));
+    assertU(adoc("id", "53", "text0", "close", "text1", ")", "text-keyword0", ")"));
+    assertU(adoc("id", "54", "text0", "escape", "text1", "\\", "text-keyword0", "\\"));
+    assertU(adoc("id", "55", "text0", "whitespace", "text1", "whitespace", "text-keyword0", " "));
+    assertU(adoc("id", "55", "text0", "whitespace", "text1", "whitespace", "text-keyword0", "\n"));
+    assertU(commit());
+  }
+
+  @Test
+  public void testQueryFields() throws Exception {
+    assertJQ(req("defType", "simple", "qf", "text0^2 text1 text-keyword0", "q", "t3"), "/response/numFound==1");
+    assertJQ(req("defType", "simple", "qf", "text0^3 text1^4 text-keyword0^0.55", "q", "t0"), "/response/numFound==4");
+    assertJQ(req("defType", "simple", "qf", "text-keyword0^9.2", "q", "\"kw9 kw10 kw11\""), "/response/numFound==1");
+    assertJQ(req("defType", "simple", "qf", "text-keyword0", "q", "kw9 kw10 kw11"), "/response/numFound==0");
+    assertJQ(req("defType", "simple", "qf", "text1 text-keyword0", "q", "kw9"), "/response/numFound==0");
+    assertJQ(req("defType", "simple", "qf", "text0", "q", "t2"), "/response/numFound==1");
+    assertJQ(req("defType", "simple", "qf", "text0^1.1 text1^0.9", "q", "t2 t9 t12"), "/response/numFound==4");
+  }
+
+  @Test
+  public void testDefaultField() throws Exception {
+    assertJQ(req("defType", "simple", "q", "t2 t9 t12"), "/response/numFound==1");
+    assertJQ(req("defType", "simple", "q", "t3"), "/response/numFound==0");
+    assertJQ(req("defType", "simple", "df", "text1", "q", "t2 t9 t12"), "/response/numFound==3");
+    assertJQ(req("defType", "simple", "df", "text1", "q", "t3"), "/response/numFound==1");
+    assertJQ(req("defType", "simple", "df", "text-keyword0", "q", "\"kw9 kw10 kw11\""), "/response/numFound==1");
+    assertJQ(req("defType", "simple", "df", "text-keyword0", "q", "kw9 kw10 kw11"), "/response/numFound==0");
+  }
+
+  @Test
+  public void testQueryFieldPriority() throws Exception {
+    assertJQ(req("defType", "simple", "qf", "text0^2 text1 text-keyword0", "df", "text0", "q", "t3"), "/response/numFound==1");
+  }
+
+  @Test
+  public void testOnlyAndOperatorEnabledDisabled() throws Exception {
+    assertJQ(req("defType", "simple", "qf", "text0 text1 text-keyword0", "q", "+",
+        "q.operators", "NOT, OR, PHRASE, PREFIX, PRECEDENCE, ESCAPE, WHITESPACE"), "/response/numFound==1");
+    assertJQ(req("defType", "simple", "qf", "text0 text1 text-keyword0", "q", "-",
+        "q.operators", "NOT, OR, PHRASE, PREFIX, PRECEDENCE, ESCAPE, WHITESPACE"), "/response/numFound==0");
+    assertJQ(req("defType", "simple", "qf", "text0 text1 text-keyword0", "q", "+",
+        "q.operators", "AND"), "/response/numFound==0");
+    assertJQ(req("defType", "simple", "qf", "text0 text1 text-keyword0", "q", "-",
+        "q.operators", "AND"), "/response/numFound==1");
+  }
+
+  @Test
+  public void testOnlyNotOperatorEnabledDisabled() throws Exception {
+    assertJQ(req("defType", "simple", "qf", "text0 text1 text-keyword0", "q", "-",
+        "q.operators", "AND, OR, PHRASE, PREFIX, PRECEDENCE, ESCAPE, WHITESPACE"), "/response/numFound==1");
+    assertJQ(req("defType", "simple", "qf", "text0 text1 text-keyword0", "q", "|",
+        "q.operators", "AND, OR, PHRASE, PREFIX, PRECEDENCE, ESCAPE, WHITESPACE"), "/response/numFound==0");
+    assertJQ(req("defType", "simple", "qf", "text0 text1 text-keyword0", "q", "-",
+        "q.operators", "NOT"), "/response/numFound==0");
+    assertJQ(req("defType", "simple", "qf", "text0 text1 text-keyword0", "q", "|",
+        "q.operators", "NOT"), "/response/numFound==1");
+  }
+
+  @Test
+  public void testOnlyOrOperatorEnabledDisabled() throws Exception {
+    assertJQ(req("defType", "simple", "qf", "text0 text1 text-keyword0", "q", "|",
+        "q.operators", "AND, NOT, PHRASE, PREFIX, PRECEDENCE, ESCAPE, WHITESPACE"), "/response/numFound==1");
+    assertJQ(req("defType", "simple", "qf", "text0 text1 text-keyword0", "q", "\"",
+        "q.operators", "AND, NOT, PHRASE, PREFIX, PRECEDENCE, ESCAPE, WHITESPACE"), "/response/numFound==0");
+    assertJQ(req("defType", "simple", "qf", "text0 text1 text-keyword0", "q", "|",
+        "q.operators", "OR"), "/response/numFound==0");
+    assertJQ(req("defType", "simple", "qf", "text0 text1 text-keyword0", "q", "\"",
+        "q.operators", "OR"), "/response/numFound==1");
+  }
+
+  @Test
+  public void testOnlyPhraseOperatorEnabledDisabled() throws Exception {
+    assertJQ(req("defType", "simple", "qf", "text0 text1 text-keyword0", "q", "\"",
+        "q.operators", "AND, NOT, OR, PREFIX, PRECEDENCE, ESCAPE, WHITESPACE"), "/response/numFound==1");
+    assertJQ(req("defType", "simple", "qf", "text0 text1 text-keyword0", "q", "|",
+        "q.operators", "AND, NOT, OR, PREFIX, PRECEDENCE, ESCAPE, WHITESPACE"), "/response/numFound==0");
+    assertJQ(req("defType", "simple", "qf", "text0 text1 text-keyword0", "q", "\"",
+        "q.operators", "PHRASE"), "/response/numFound==0");
+    assertJQ(req("defType", "simple", "qf", "text0 text1 text-keyword0", "q", "|",
+        "q.operators", "PHRASE"), "/response/numFound==1");
+  }
+
+  @Test
+  public void testOnlyPrefixOperatorEnabledDisabled() throws Exception {
+    assertJQ(req("defType", "simple", "qf", "text0 text1 text-keyword0", "q", "t*",
+        "q.operators", "AND, NOT, OR, PHRASE, PRECEDENCE, ESCAPE, WHITESPACE"), "/response/numFound==1");
+    assertJQ(req("defType", "simple", "qf", "text0 text1 text-keyword0", "q", "(",
+        "q.operators", "AND, NOT, OR, PHRASE, PRECEDENCE, ESCAPE, WHITESPACE"), "/response/numFound==0");
+    assertJQ(req("defType", "simple", "qf", "text0 text1 text-keyword0", "q", "t*",
+        "q.operators", "PREFIX"), "/response/numFound==6");
+    assertJQ(req("defType", "simple", "qf", "text0 text1 text-keyword0", "q", "(",
+        "q.operators", "PREFIX"), "/response/numFound==1");
+  }
+
+  @Test
+  public void testOnlyPrecedenceOperatorEnabledDisabled() throws Exception {
+    assertJQ(req("defType", "simple", "qf", "text0 text1 text-keyword0", "q", "(",
+        "q.operators", "AND, NOT, OR, PHRASE, PREFIX, ESCAPE, WHITESPACE"), "/response/numFound==1");
+    assertJQ(req("defType", "simple", "qf", "text0 text1 text-keyword0", "q", "\\",
+        "q.operators", "AND, NOT, OR, PHRASE, PREFIX, ESCAPE, WHITESPACE"), "/response/numFound==0");
+    assertJQ(req("defType", "simple", "qf", "text0 text1 text-keyword0", "q", "(",
+        "q.operators", "PRECEDENCE"), "/response/numFound==0");
+    assertJQ(req("defType", "simple", "qf", "text0 text1 text-keyword0", "q", "\\",
+        "q.operators", "PRECEDENCE"), "/response/numFound==1");
+
+    assertJQ(req("defType", "simple", "qf", "text0 text1 text-keyword0", "q", ")",
+        "q.operators", "AND, NOT, OR, PHRASE, PREFIX, ESCAPE, WHITESPACE"), "/response/numFound==1");
+    assertJQ(req("defType", "simple", "qf", "text0 text1 text-keyword0", "q", "\\",
+        "q.operators", "AND, NOT, OR, PHRASE, PREFIX, ESCAPE, WHITESPACE"), "/response/numFound==0");
+    assertJQ(req("defType", "simple", "qf", "text0 text1 text-keyword0", "q", ")",
+        "q.operators", "PRECEDENCE"), "/response/numFound==0");
+    assertJQ(req("defType", "simple", "qf", "text0 text1 text-keyword0", "q", "\\",
+        "q.operators", "PRECEDENCE"), "/response/numFound==1");
+  }
+
+  @Test
+  public void testOnlyEscapeOperatorEnabledDisabled() throws Exception {
+    assertJQ(req("defType", "simple", "qf", "text0 text1 text-keyword0", "q", "\\",
+        "q.operators", "AND, NOT, OR, PHRASE, PREFIX, PRECEDENCE, WHITESPACE"), "/response/numFound==1");
+    assertJQ(req("defType", "simple", "qf", "text0 text1 text-keyword0", "q", "\n",
+        "q.operators", "AND, NOT, OR, PHRASE, PREFIX, PRECEDENCE, WHITESPACE"), "/response/numFound==0");
+    assertJQ(req("defType", "simple", "qf", "text0 text1 text-keyword0", "q", "\\",
+        "q.operators", "ESCAPE"), "/response/numFound==0");
+    assertJQ(req("defType", "simple", "qf", "text0 text1 text-keyword0", "q", "\n",
+        "q.operators", "ESCAPE"), "/response/numFound==1");
+  }
+
+  @Test
+  public void testOnlyWhitespaceOperatorEnabledDisabled() throws Exception {
+    assertJQ(req("defType", "simple", "qf", "text0 text1 text-keyword0", "q", "\n",
+        "q.operators", "AND, NOT, OR, PHRASE, PREFIX, PRECEDENCE, ESCAPE"), "/response/numFound==1");
+    assertJQ(req("defType", "simple", "qf", "text0 text1 text-keyword0", "q", "\\",
+        "q.operators", "AND, NOT, OR, PHRASE, PREFIX, PRECEDENCE, ESCAPE"), "/response/numFound==0");
+    assertJQ(req("defType", "simple", "qf", "text0 text1 text-keyword0", "q", "\n",
+        "q.operators", "WHITESPACE"), "/response/numFound==0");
+    assertJQ(req("defType", "simple", "qf", "text0 text1 text-keyword0", "q", "\\",
+        "q.operators", "WHITESPACE"), "/response/numFound==1");
+  }
+
+  @Test
+  public void testArbitraryOperatorsEnabledDisabled() throws Exception {
+    assertJQ(req("defType", "simple", "qf", "text0 text1 text-keyword0", "q", "kw0+kw1+kw2| \\ ",
+        "q.operators", "AND, NOT, OR, PHRASE"), "/response/numFound==1");
+    assertJQ(req("defType", "simple", "qf", "text0 text1 text-keyword0", "q", "t1 + t2 \\",
+        "q.operators", "AND, WHITESPACE"), "/response/numFound==3");
+    assertJQ(req("defType", "simple", "qf", "text0 text-keyword0", "q", "t0 + (-t1 -t2) |",
+        "q.operators", "AND, NOT, PRECEDENCE, WHITESPACE"), "/response/numFound==4");
+  }
+
+  @Test
+  public void testNoOperators() throws Exception {
+    assertJQ(req("defType", "simple", "qf", "text1 text-keyword0", "q", "kw0 kw1 kw2",
+        "q.operators", ""), "/response/numFound==1");
+    assertJQ(req("defType", "simple", "qf", "text1", "q", "t1 t2 t3",
+        "q.operators", ""), "/response/numFound==2");
+  }
+
+  @Test
+  public void testDefaultOperator() throws Exception {
+    assertJQ(req("defType", "simple", "qf", "text1 text-keyword0", "q", "t2 t3",
+        "q.op", "AND"), "/response/numFound==0");
+    assertJQ(req("defType", "simple", "qf", "text0 text-keyword0", "q", "t0 t2",
+        "q.op", "AND"), "/response/numFound==1");
+    assertJQ(req("defType", "simple", "qf", "text1", "q", "t2 t3"), "/response/numFound==2");
+  }
+}

Added: lucene/dev/trunk/solr/solrj/src/java/org/apache/solr/common/params/SimpleParams.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/solrj/src/java/org/apache/solr/common/params/SimpleParams.java?rev=1542486&view=auto
==============================================================================
--- lucene/dev/trunk/solr/solrj/src/java/org/apache/solr/common/params/SimpleParams.java (added)
+++ lucene/dev/trunk/solr/solrj/src/java/org/apache/solr/common/params/SimpleParams.java Sat Nov 16 09:16:57 2013
@@ -0,0 +1,29 @@
+package org.apache.solr.common.params;
+
+/*
+ * 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.
+ */
+
+/**
+ * Parameters used by the SimpleQParser.
+ */
+public interface SimpleParams {
+  /** Query fields and boosts. */
+  public static String QF = "qf";
+
+  /** Override the currently enabled/disabled query operators. */
+  public static String QO = "q.operators";
+}