diff --git a/lucene/CHANGES.txt b/lucene/CHANGES.txt index 58629fb2427..e49b5854270 100644 --- a/lucene/CHANGES.txt +++ b/lucene/CHANGES.txt @@ -125,6 +125,9 @@ New Features * LUCENE-5320: Add SearcherTaxonomyManager over search and taxonomy index directories (i.e. not only NRT). (Shai Erera) +* LUCENE-5410: Add fuzzy and near support via '~' operator to SimpleQueryParser. + (Lee Hinman via Robert Muir) + Build * LUCENE-5217,LUCENE-5420: Maven config: get dependencies from Ant+Ivy config; diff --git a/lucene/core/src/java/org/apache/lucene/search/MultiPhraseQuery.java b/lucene/core/src/java/org/apache/lucene/search/MultiPhraseQuery.java index c46ec482e9d..b48accc7e98 100644 --- a/lucene/core/src/java/org/apache/lucene/search/MultiPhraseQuery.java +++ b/lucene/core/src/java/org/apache/lucene/search/MultiPhraseQuery.java @@ -58,7 +58,12 @@ public class MultiPhraseQuery extends Query { /** Sets the phrase slop for this query. * @see PhraseQuery#setSlop(int) */ - public void setSlop(int s) { slop = s; } + public void setSlop(int s) { + if (s < 0) { + throw new IllegalArgumentException("slop value cannot be negative"); + } + slop = s; + } /** Sets the phrase slop for this query. * @see PhraseQuery#getSlop() diff --git a/lucene/core/src/java/org/apache/lucene/search/PhraseQuery.java b/lucene/core/src/java/org/apache/lucene/search/PhraseQuery.java index 6a32d51578f..d8de14240c6 100644 --- a/lucene/core/src/java/org/apache/lucene/search/PhraseQuery.java +++ b/lucene/core/src/java/org/apache/lucene/search/PhraseQuery.java @@ -68,7 +68,12 @@ public class PhraseQuery extends Query { results are sorted by exactness.

The slop is zero by default, requiring exact matches.*/ - public void setSlop(int s) { slop = s; } + public void setSlop(int s) { + if (s < 0) { + throw new IllegalArgumentException("slop value cannot be negative"); + } + slop = s; + } /** Returns the slop. See setSlop(). */ public int getSlop() { return slop; } diff --git a/lucene/core/src/test/org/apache/lucene/search/TestMultiPhraseQuery.java b/lucene/core/src/test/org/apache/lucene/search/TestMultiPhraseQuery.java index f9f56c0202e..392dc6b6c4e 100644 --- a/lucene/core/src/test/org/apache/lucene/search/TestMultiPhraseQuery.java +++ b/lucene/core/src/test/org/apache/lucene/search/TestMultiPhraseQuery.java @@ -568,4 +568,16 @@ public class TestMultiPhraseQuery extends LuceneTestCase { return terms; } + public void testNegativeSlop() throws Exception { + MultiPhraseQuery query = new MultiPhraseQuery(); + query.add(new Term("field", "two")); + query.add(new Term("field", "one")); + try { + query.setSlop(-2); + fail("didn't get expected exception"); + } catch (IllegalArgumentException expected) { + // expected exception + } + } + } diff --git a/lucene/core/src/test/org/apache/lucene/search/TestPhraseQuery.java b/lucene/core/src/test/org/apache/lucene/search/TestPhraseQuery.java index 0ec884bdd21..a2dbdadc2a9 100644 --- a/lucene/core/src/test/org/apache/lucene/search/TestPhraseQuery.java +++ b/lucene/core/src/test/org/apache/lucene/search/TestPhraseQuery.java @@ -678,4 +678,16 @@ public class TestPhraseQuery extends LuceneTestCase { reader.close(); dir.close(); } + + public void testNegativeSlop() throws Exception { + PhraseQuery query = new PhraseQuery(); + query.add(new Term("field", "two")); + query.add(new Term("field", "one")); + try { + query.setSlop(-2); + fail("didn't get expected exception"); + } catch (IllegalArgumentException expected) { + // expected exception + } + } } diff --git a/lucene/queryparser/src/java/org/apache/lucene/queryparser/simple/SimpleQueryParser.java b/lucene/queryparser/src/java/org/apache/lucene/queryparser/simple/SimpleQueryParser.java index 908c1baf116..49d4c512cb9 100644 --- a/lucene/queryparser/src/java/org/apache/lucene/queryparser/simple/SimpleQueryParser.java +++ b/lucene/queryparser/src/java/org/apache/lucene/queryparser/simple/SimpleQueryParser.java @@ -21,10 +21,12 @@ import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.index.Term; import org.apache.lucene.search.BooleanClause; import org.apache.lucene.search.BooleanQuery; +import org.apache.lucene.search.FuzzyQuery; import org.apache.lucene.search.MatchAllDocsQuery; import org.apache.lucene.search.PrefixQuery; import org.apache.lucene.search.Query; import org.apache.lucene.util.QueryBuilder; +import org.apache.lucene.util.automaton.LevenshteinAutomata; import java.util.Collections; import java.util.Map; @@ -48,6 +50,8 @@ import java.util.Map; *

  • '{@code -}' negates a single token: -token0 *
  • '{@code "}' creates phrases of terms: "term1 term2 ..." *
  • '{@code *}' at the end of terms specifies prefix query: term* + *
  • '{@code ~}N' at the end of terms specifies fuzzy query: term~1 + *
  • '{@code ~}N' at the end of phrases specifies near query: "term1 term2"~5 *
  • '{@code (}' and '{@code )}' specifies precedence: token1 + (token2 | token3) * *

    @@ -111,6 +115,11 @@ public class SimpleQueryParser extends QueryBuilder { public static final int ESCAPE_OPERATOR = 1<<6; /** Enables {@code WHITESPACE} operators: ' ' '\n' '\r' '\t' */ public static final int WHITESPACE_OPERATOR = 1<<7; + /** Enables {@code FUZZY} operators: (~) on single terms */ + public static final int FUZZY_OPERATOR = 1<<8; + /** Enables {@code NEAR} operators: (~) on phrases */ + public static final int NEAR_OPERATOR = 1<<9; + private BooleanClause.Occur defaultOperator = BooleanClause.Occur.SHOULD; @@ -266,6 +275,7 @@ public class SimpleQueryParser extends QueryBuilder { int start = ++state.index; int copied = 0; boolean escaped = false; + boolean hasSlop = false; while (state.index < state.length) { if (!escaped) { @@ -279,10 +289,23 @@ public class SimpleQueryParser extends QueryBuilder { continue; } else if (state.data[state.index] == '"') { - // this should be the end of the phrase - // all characters found will used for - // creating the phrase query - break; + // if there are still characters after the closing ", check for a + // tilde + if (state.length > (state.index + 1) && + state.data[state.index+1] == '~' && + (flags & NEAR_OPERATOR) != 0) { + state.index++; + // check for characters after the tilde + if (state.length > (state.index + 1)) { + hasSlop = true; + } + break; + } else { + // this should be the end of the phrase + // all characters found will used for + // creating the phrase query + break; + } } } @@ -305,7 +328,12 @@ public class SimpleQueryParser extends QueryBuilder { // a complete phrase has been found and is parsed through // through the analyzer from the given field String phrase = new String(state.buffer, 0, copied); - Query branch = newPhraseQuery(phrase); + Query branch; + if (hasSlop) { + branch = newPhraseQuery(phrase, parseFuzziness(state)); + } else { + branch = newPhraseQuery(phrase, 0); + } buildQueryTree(state, branch); ++state.index; @@ -316,6 +344,7 @@ public class SimpleQueryParser extends QueryBuilder { int copied = 0; boolean escaped = false; boolean prefix = false; + boolean fuzzy = false; while (state.index < state.length) { if (!escaped) { @@ -329,19 +358,14 @@ public class SimpleQueryParser extends QueryBuilder { ++state.index; continue; - } else if ((state.data[state.index] == '"' && (flags & PHRASE_OPERATOR) != 0) - || (state.data[state.index] == '|' && (flags & OR_OPERATOR) != 0) - || (state.data[state.index] == '+' && (flags & AND_OPERATOR) != 0) - || (state.data[state.index] == '(' && (flags & PRECEDENCE_OPERATORS) != 0) - || (state.data[state.index] == ')' && (flags & PRECEDENCE_OPERATORS) != 0) - || ((state.data[state.index] == ' ' - || state.data[state.index] == '\t' - || state.data[state.index] == '\n' - || state.data[state.index] == '\r') && (flags & WHITESPACE_OPERATOR) != 0)) { + } else if (tokenFinished(state)) { // this should be the end of the term // all characters found will used for // creating the term query break; + } else if (copied > 0 && state.data[state.index] == '~' && (flags & FUZZY_OPERATOR) != 0) { + fuzzy = true; + break; } // wildcard tracks whether or not the last character @@ -358,7 +382,17 @@ public class SimpleQueryParser extends QueryBuilder { if (copied > 0) { final Query branch; - if (prefix) { + if (fuzzy && (flags & FUZZY_OPERATOR) != 0) { + String token = new String(state.buffer, 0, copied); + int fuzziness = parseFuzziness(state); + // edit distance has a maximum, limit to the maximum supported + fuzziness = Math.min(fuzziness, LevenshteinAutomata.MAXIMUM_SUPPORTED_DISTANCE); + if (fuzziness == 0) { + branch = newDefaultQuery(token); + } else { + branch = newFuzzyQuery(token, fuzziness); + } + } else if (prefix) { // if a term is found with a closing '*' it is considered to be a prefix query // and will have prefix added as an option String token = new String(state.buffer, 0, copied - 1); @@ -420,6 +454,60 @@ public class SimpleQueryParser extends QueryBuilder { } } + /** + * Helper parsing fuzziness from parsing state + * @return slop/edit distance, 0 in the case of non-parsing slop/edit string + */ + private int parseFuzziness(State state) { + char slopText[] = new char[state.length]; + int slopLength = 0; + + if (state.data[state.index] == '~') { + while (state.index < state.length) { + state.index++; + // it's possible that the ~ was at the end, so check after incrementing + // to make sure we don't go out of bounds + if (state.index < state.length) { + if (tokenFinished(state)) { + break; + } + slopText[slopLength] = state.data[state.index]; + slopLength++; + } + } + int fuzziness = 0; + try { + fuzziness = Integer.parseInt(new String(slopText, 0, slopLength)); + } catch (NumberFormatException e) { + // swallow number format exceptions parsing fuzziness + } + // negative -> 0 + if (fuzziness < 0) { + fuzziness = 0; + } + return fuzziness; + } + return 0; + } + + /** + * Helper returning true if the state has reached the end of token. + */ + private boolean tokenFinished(State state) { + if ((state.data[state.index] == '"' && (flags & PHRASE_OPERATOR) != 0) + || (state.data[state.index] == '|' && (flags & OR_OPERATOR) != 0) + || (state.data[state.index] == '+' && (flags & AND_OPERATOR) != 0) + || (state.data[state.index] == '(' && (flags & PRECEDENCE_OPERATORS) != 0) + || (state.data[state.index] == ')' && (flags & PRECEDENCE_OPERATORS) != 0) + || ((state.data[state.index] == ' ' + || state.data[state.index] == '\t' + || state.data[state.index] == '\n' + || state.data[state.index] == '\r') && (flags & WHITESPACE_OPERATOR) != 0)) { + return true; + } + return false; + } + /** * Factory method to generate a standard query (no phrase or prefix operators). */ @@ -436,12 +524,27 @@ public class SimpleQueryParser extends QueryBuilder { } /** - * Factory method to generate a phrase query. + * Factory method to generate a fuzzy query. */ - protected Query newPhraseQuery(String text) { + protected Query newFuzzyQuery(String text, int fuzziness) { BooleanQuery bq = new BooleanQuery(true); for (Map.Entry entry : weights.entrySet()) { - Query q = createPhraseQuery(entry.getKey(), text); + Query q = new FuzzyQuery(new Term(entry.getKey(), text), fuzziness); + if (q != null) { + q.setBoost(entry.getValue()); + bq.add(q, BooleanClause.Occur.SHOULD); + } + } + return simplify(bq); + } + + /** + * Factory method to generate a phrase query with slop. + */ + protected Query newPhraseQuery(String text, int slop) { + BooleanQuery bq = new BooleanQuery(true); + for (Map.Entry entry : weights.entrySet()) { + Query q = createPhraseQuery(entry.getKey(), text, slop); if (q != null) { q.setBoost(entry.getValue()); bq.add(q, BooleanClause.Occur.SHOULD); diff --git a/lucene/queryparser/src/test/org/apache/lucene/queryparser/simple/TestSimpleQueryParser.java b/lucene/queryparser/src/test/org/apache/lucene/queryparser/simple/TestSimpleQueryParser.java index 078defbfecc..c852a069296 100644 --- a/lucene/queryparser/src/test/org/apache/lucene/queryparser/simple/TestSimpleQueryParser.java +++ b/lucene/queryparser/src/test/org/apache/lucene/queryparser/simple/TestSimpleQueryParser.java @@ -27,6 +27,7 @@ import org.apache.lucene.analysis.MockTokenizer; import org.apache.lucene.index.Term; import org.apache.lucene.search.BooleanClause.Occur; import org.apache.lucene.search.BooleanQuery; +import org.apache.lucene.search.FuzzyQuery; import org.apache.lucene.search.MatchAllDocsQuery; import org.apache.lucene.search.PhraseQuery; import org.apache.lucene.search.PrefixQuery; @@ -34,14 +35,17 @@ import org.apache.lucene.search.Query; import org.apache.lucene.search.TermQuery; import org.apache.lucene.util.LuceneTestCase; import org.apache.lucene.util._TestUtil; +import org.apache.lucene.util.automaton.LevenshteinAutomata; import static org.apache.lucene.queryparser.simple.SimpleQueryParser.AND_OPERATOR; import static org.apache.lucene.queryparser.simple.SimpleQueryParser.ESCAPE_OPERATOR; +import static org.apache.lucene.queryparser.simple.SimpleQueryParser.FUZZY_OPERATOR; import static org.apache.lucene.queryparser.simple.SimpleQueryParser.NOT_OPERATOR; import static org.apache.lucene.queryparser.simple.SimpleQueryParser.OR_OPERATOR; import static org.apache.lucene.queryparser.simple.SimpleQueryParser.PHRASE_OPERATOR; import static org.apache.lucene.queryparser.simple.SimpleQueryParser.PRECEDENCE_OPERATORS; import static org.apache.lucene.queryparser.simple.SimpleQueryParser.PREFIX_OPERATOR; +import static org.apache.lucene.queryparser.simple.SimpleQueryParser.NEAR_OPERATOR; import static org.apache.lucene.queryparser.simple.SimpleQueryParser.WHITESPACE_OPERATOR; /** Tests for {@link SimpleQueryParser} */ @@ -58,6 +62,18 @@ public class TestSimpleQueryParser extends LuceneTestCase { return parser.parse(text); } + /** + * helper to parse a query with whitespace+lowercase analyzer across "field", + * with default operator of MUST + */ + private Query parse(String text, int flags) { + Analyzer analyzer = new MockAnalyzer(random()); + SimpleQueryParser parser = new SimpleQueryParser(analyzer, + Collections.singletonMap("field", 1f), flags); + parser.setDefaultOperator(Occur.MUST); + return parser.parse(text); + } + /** test a simple term */ public void testTerm() throws Exception { Query expected = new TermQuery(new Term("field", "foobar")); @@ -65,6 +81,24 @@ public class TestSimpleQueryParser extends LuceneTestCase { assertEquals(expected, parse("foobar")); } + /** test a fuzzy query */ + public void testFuzzy() throws Exception { + Query regular = new TermQuery(new Term("field", "foobar")); + Query expected = new FuzzyQuery(new Term("field", "foobar"), 2); + + assertEquals(expected, parse("foobar~2")); + assertEquals(regular, parse("foobar~")); + assertEquals(regular, parse("foobar~a")); + assertEquals(regular, parse("foobar~1a")); + + BooleanQuery bool = new BooleanQuery(); + FuzzyQuery fuzzy = new FuzzyQuery(new Term("field", "foo"), LevenshteinAutomata.MAXIMUM_SUPPORTED_DISTANCE); + bool.add(fuzzy, Occur.MUST); + bool.add(new TermQuery(new Term("field", "bar")), Occur.MUST); + + assertEquals(bool, parse("foo~" + LevenshteinAutomata.MAXIMUM_SUPPORTED_DISTANCE + 1 + " bar")); + } + /** test a simple phrase */ public void testPhrase() throws Exception { PhraseQuery expected = new PhraseQuery(); @@ -74,6 +108,43 @@ public class TestSimpleQueryParser extends LuceneTestCase { assertEquals(expected, parse("\"foo bar\"")); } + /** test a simple phrase with various slop settings */ + public void testPhraseWithSlop() throws Exception { + PhraseQuery expectedWithSlop = new PhraseQuery(); + expectedWithSlop.add(new Term("field", "foo")); + expectedWithSlop.add(new Term("field", "bar")); + expectedWithSlop.setSlop(2); + + assertEquals(expectedWithSlop, parse("\"foo bar\"~2")); + + PhraseQuery expectedWithMultiDigitSlop = new PhraseQuery(); + expectedWithMultiDigitSlop.add(new Term("field", "foo")); + expectedWithMultiDigitSlop.add(new Term("field", "bar")); + expectedWithMultiDigitSlop.setSlop(10); + + assertEquals(expectedWithMultiDigitSlop, parse("\"foo bar\"~10")); + + PhraseQuery expectedNoSlop = new PhraseQuery(); + expectedNoSlop.add(new Term("field", "foo")); + expectedNoSlop.add(new Term("field", "bar")); + + assertEquals("Ignore trailing tilde with no slop", expectedNoSlop, parse("\"foo bar\"~")); + assertEquals("Ignore non-numeric trailing slop", expectedNoSlop, parse("\"foo bar\"~a")); + assertEquals("Ignore non-numeric trailing slop", expectedNoSlop, parse("\"foo bar\"~1a")); + assertEquals("Ignore negative trailing slop", expectedNoSlop, parse("\"foo bar\"~-1")); + + PhraseQuery pq = new PhraseQuery(); + pq.add(new Term("field", "foo")); + pq.add(new Term("field", "bar")); + pq.setSlop(12); + + BooleanQuery expectedBoolean = new BooleanQuery(); + expectedBoolean.add(pq, Occur.MUST); + expectedBoolean.add(new TermQuery(new Term("field", "baz")), Occur.MUST); + + assertEquals(expectedBoolean, parse("\"foo bar\"~12 baz")); + } + /** test a simple prefix */ public void testPrefix() throws Exception { PrefixQuery expected = new PrefixQuery(new Term("field", "foobar")); @@ -533,17 +604,33 @@ public class TestSimpleQueryParser extends LuceneTestCase { assertEquals(expected, parseKeyword("\t\tfoo foo foo", ~WHITESPACE_OPERATOR)); } + public void testDisableFuzziness() { + Query expected = new TermQuery(new Term("field", "foo~1")); + assertEquals(expected, parseKeyword("foo~1", ~FUZZY_OPERATOR)); + } + + public void testDisableSlop() { + PhraseQuery expectedPhrase = new PhraseQuery(); + expectedPhrase.add(new Term("field", "foo")); + expectedPhrase.add(new Term("field", "bar")); + + BooleanQuery expected = new BooleanQuery(); + expected.add(expectedPhrase, Occur.MUST); + expected.add(new TermQuery(new Term("field", "~2")), Occur.MUST); + assertEquals(expected, parse("\"foo bar\"~2", ~NEAR_OPERATOR)); + } + // we aren't supposed to barf on any input... public void testRandomQueries() throws Exception { for (int i = 0; i < 1000; i++) { String query = _TestUtil.randomUnicodeString(random()); parse(query); // no exception - parseKeyword(query, _TestUtil.nextInt(random(), 0, 256)); // no exception + parseKeyword(query, _TestUtil.nextInt(random(), 0, 1024)); // no exception } } public void testRandomQueries2() throws Exception { - char chars[] = new char[] { 'a', '1', '|', '&', ' ', '(', ')', '"', '-' }; + char chars[] = new char[] { 'a', '1', '|', '&', ' ', '(', ')', '"', '-', '~'}; StringBuilder sb = new StringBuilder(); for (int i = 0; i < 1000; i++) { sb.setLength(0); @@ -552,7 +639,7 @@ public class TestSimpleQueryParser extends LuceneTestCase { sb.append(chars[random().nextInt(chars.length)]); } parse(sb.toString()); // no exception - parseKeyword(sb.toString(), _TestUtil.nextInt(random(), 0, 256)); // no exception + parseKeyword(sb.toString(), _TestUtil.nextInt(random(), 0, 1024)); // no exception } } } \ No newline at end of file diff --git a/solr/core/src/java/org/apache/solr/search/SimpleQParserPlugin.java b/solr/core/src/java/org/apache/solr/search/SimpleQParserPlugin.java index cc75ace4bf2..db8b822804a 100644 --- a/solr/core/src/java/org/apache/solr/search/SimpleQParserPlugin.java +++ b/solr/core/src/java/org/apache/solr/search/SimpleQParserPlugin.java @@ -18,11 +18,9 @@ package org.apache.solr.search; */ import org.apache.lucene.analysis.Analyzer; -import org.apache.lucene.index.Term; import org.apache.lucene.queryparser.simple.SimpleQueryParser; import org.apache.lucene.search.BooleanClause; import org.apache.lucene.search.BooleanQuery; -import org.apache.lucene.search.PrefixQuery; import org.apache.lucene.search.Query; import org.apache.solr.common.params.CommonParams; import org.apache.solr.common.params.SimpleParams; @@ -73,36 +71,21 @@ 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); + OPERATORS.put(SimpleParams.AND_OPERATOR, SimpleQueryParser.AND_OPERATOR); + OPERATORS.put(SimpleParams.NOT_OPERATOR, SimpleQueryParser.NOT_OPERATOR); + OPERATORS.put(SimpleParams.OR_OPERATOR, SimpleQueryParser.OR_OPERATOR); + OPERATORS.put(SimpleParams.PREFIX_OPERATOR, SimpleQueryParser.PREFIX_OPERATOR); + OPERATORS.put(SimpleParams.PHRASE_OPERATOR, SimpleQueryParser.PHRASE_OPERATOR); + OPERATORS.put(SimpleParams.PRECEDENCE_OPERATORS, SimpleQueryParser.PRECEDENCE_OPERATORS); + OPERATORS.put(SimpleParams.ESCAPE_OPERATOR, SimpleQueryParser.ESCAPE_OPERATOR); + OPERATORS.put(SimpleParams.WHITESPACE_OPERATOR, SimpleQueryParser.WHITESPACE_OPERATOR); + OPERATORS.put(SimpleParams.FUZZY_OPERATOR, SimpleQueryParser.FUZZY_OPERATOR); + OPERATORS.put(SimpleParams.NEAR_OPERATOR, SimpleQueryParser.NEAR_OPERATOR); } /** No initialization is necessary so this method is empty. */ 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 index 1fd89030f30..e7704027b5e 100644 --- a/solr/solrj/src/java/org/apache/solr/common/params/SimpleParams.java +++ b/solr/solrj/src/java/org/apache/solr/common/params/SimpleParams.java @@ -26,4 +26,25 @@ public interface SimpleParams { /** Override the currently enabled/disabled query operators. */ public static String QO = "q.operators"; + + /** Enables {@code AND} operator (+) */ + public static final String AND_OPERATOR = "AND"; + /** Enables {@code NOT} operator (-) */ + public static final String NOT_OPERATOR = "NOT"; + /** Enables {@code OR} operator (|) */ + public static final String OR_OPERATOR = "OR"; + /** Enables {@code PREFIX} operator (*) */ + public static final String PREFIX_OPERATOR = "PREFIX"; + /** Enables {@code PHRASE} operator (") */ + public static final String PHRASE_OPERATOR = "PHRASE"; + /** Enables {@code PRECEDENCE} operators: {@code (} and {@code )} */ + public static final String PRECEDENCE_OPERATORS = "PRECEDENCE"; + /** Enables {@code ESCAPE} operator (\) */ + public static final String ESCAPE_OPERATOR = "ESCAPE"; + /** Enables {@code WHITESPACE} operators: ' ' '\n' '\r' '\t' */ + public static final String WHITESPACE_OPERATOR = "WHITESPACE"; + /** Enables {@code FUZZY} operator (~) */ + public static final String FUZZY_OPERATOR = "FUZZY"; + /** Enables {@code NEAR} operator (~) */ + public static final String NEAR_OPERATOR = "NEAR"; }