diff --git a/solr/CHANGES.txt b/solr/CHANGES.txt index eedba58ca73..97eea1c638c 100644 --- a/solr/CHANGES.txt +++ b/solr/CHANGES.txt @@ -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 ---------------------- diff --git a/solr/core/src/java/org/apache/solr/search/QParserPlugin.java b/solr/core/src/java/org/apache/solr/search/QParserPlugin.java index 4cbe8b38dfc..a0f572d2a86 100644 --- a/solr/core/src/java/org/apache/solr/search/QParserPlugin.java +++ b/solr/core/src/java/org/apache/solr/search/QParserPlugin.java @@ -52,7 +52,8 @@ public abstract class QParserPlugin implements NamedListInitializedPlugin, SolrI 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} */ diff --git a/solr/core/src/java/org/apache/solr/search/SimpleQParserPlugin.java b/solr/core/src/java/org/apache/solr/search/SimpleQParserPlugin.java new file mode 100644 index 00000000000..5e5c6e837ad --- /dev/null +++ b/solr/core/src/java/org/apache/solr/search/SimpleQParserPlugin.java @@ -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. + *
+ * The following options may be applied for parsing the query. + * + */ +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 OPERATORS = new HashMap(); + + /* 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 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 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); + } + }; + } +} diff --git a/solr/core/src/test-files/solr/collection1/conf/schema-simpleqpplugin.xml b/solr/core/src/test-files/solr/collection1/conf/schema-simpleqpplugin.xml new file mode 100644 index 00000000000..e6724421c91 --- /dev/null +++ b/solr/core/src/test-files/solr/collection1/conf/schema-simpleqpplugin.xml @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text0 + id + diff --git a/solr/core/src/test/org/apache/solr/search/TestSimpleQParserPlugin.java b/solr/core/src/test/org/apache/solr/search/TestSimpleQParserPlugin.java new file mode 100644 index 00000000000..0e2a0a502e3 --- /dev/null +++ b/solr/core/src/test/org/apache/solr/search/TestSimpleQParserPlugin.java @@ -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"); + } +} diff --git a/solr/solrj/src/java/org/apache/solr/common/params/SimpleParams.java b/solr/solrj/src/java/org/apache/solr/common/params/SimpleParams.java new file mode 100644 index 00000000000..1fd89030f30 --- /dev/null +++ b/solr/solrj/src/java/org/apache/solr/common/params/SimpleParams.java @@ -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"; +}