Browse Source

Expose `simple_query_string` flags in `flags` parameter

Lee Hinman 11 years ago
parent
commit
5463f7953f

+ 22 - 0
docs/reference/query-dsl/queries/simple-query-string-query.asciidoc

@@ -36,6 +36,9 @@ with default operator of `AND`, the same query is translated to
 
 |`analyzer` |The analyzer used to analyze each term of the query when
 creating composite queries.
+
+|`flags` |Flags specifying which features of the `simple_query_string` to
+enable. Defaults to `ALL`.
 |=======================================================================
 
 [float]
@@ -76,3 +79,22 @@ introduced fields included). For example:
     }
 }
 --------------------------------------------------
+
+[float]
+==== Flags
+`simple_query_string` support multiple flags to specify which parsing features
+should be enabled. It is specified as a `|`-delimited string with the
+`flags` parameter:
+
+[source,js]
+--------------------------------------------------
+{
+    "simple_query_string" : {
+        "query" : "foo | bar  & baz*",
+        "flags" : "OR|AND|PREFIX"
+    }
+}
+--------------------------------------------------
+
+The available flags are: `ALL`, `NONE`, `AND`, `OR`, `PREFIX`, `PHRASE`,
+`PRECEDENCE`, `ESCAPE`, and `WHITESPACE`.

+ 21 - 0
src/main/java/org/elasticsearch/index/query/SimpleQueryStringBuilder.java

@@ -35,6 +35,7 @@ public class SimpleQueryStringBuilder extends BaseQueryBuilder {
     private String analyzer;
     private Operator operator;
     private final String queryText;
+    private int flags = -1;
 
     /**
      * Operators for the default_operator
@@ -84,6 +85,22 @@ public class SimpleQueryStringBuilder extends BaseQueryBuilder {
         return this;
     }
 
+    /**
+     * Specify the enabled features of the SimpleQueryString.
+     */
+    public SimpleQueryStringBuilder flags(SimpleQueryStringFlag... flags) {
+        int value = 0;
+        if (flags.length == 0) {
+            value = SimpleQueryStringFlag.ALL.value;
+        } else {
+            for (SimpleQueryStringFlag flag : flags) {
+                value |= flag.value;
+            }
+        }
+        this.flags = value;
+        return this;
+    }
+
     @Override
     public void doXContent(XContentBuilder builder, Params params) throws IOException {
         builder.startObject(SimpleQueryStringParser.NAME);
@@ -104,6 +121,10 @@ public class SimpleQueryStringBuilder extends BaseQueryBuilder {
             builder.endArray();
         }
 
+        if (flags != -1) {
+            builder.field("flags", flags);
+        }
+
         if (analyzer != null) {
             builder.field("analyzer", analyzer);
         }

+ 58 - 0
src/main/java/org/elasticsearch/index/query/SimpleQueryStringFlag.java

@@ -0,0 +1,58 @@
+package org.elasticsearch.index.query;
+
+import org.apache.lucene.queryparser.XSimpleQueryParser;
+import org.elasticsearch.ElasticSearchIllegalArgumentException;
+import org.elasticsearch.common.Strings;
+
+import java.util.Locale;
+
+/**
+ * Flags for the XSimpleQueryString parser
+ */
+public enum SimpleQueryStringFlag {
+    ALL(-1),
+    NONE(0),
+    AND(XSimpleQueryParser.AND_OPERATOR),
+    NOT(XSimpleQueryParser.NOT_OPERATOR),
+    OR(XSimpleQueryParser.OR_OPERATOR),
+    PREFIX(XSimpleQueryParser.PREFIX_OPERATOR),
+    PRECEDENCE(XSimpleQueryParser.PRECEDENCE_OPERATORS),
+    ESCAPE(XSimpleQueryParser.ESCAPE_OPERATOR),
+    WHITESPACE(XSimpleQueryParser.WHITESPACE_OPERATOR);
+
+    final int value;
+
+    private SimpleQueryStringFlag(int value) {
+        this.value = value;
+    }
+
+    public int value() {
+        return value;
+    }
+
+    static int resolveFlags(String flags) {
+        if (!Strings.hasLength(flags)) {
+            return ALL.value();
+        }
+        int magic = NONE.value();
+        for (String s : Strings.delimitedListToStringArray(flags, "|")) {
+            if (s.isEmpty()) {
+                continue;
+            }
+            try {
+                SimpleQueryStringFlag flag = SimpleQueryStringFlag.valueOf(s.toUpperCase(Locale.ROOT));
+                switch (flag) {
+                    case NONE:
+                        return 0;
+                    case ALL:
+                        return -1;
+                    default:
+                        magic |= flag.value();
+                }
+            } catch (IllegalArgumentException iae) {
+                throw new ElasticSearchIllegalArgumentException("Unknown " + SimpleQueryStringParser.NAME + " flag [" + s + "]");
+            }
+        }
+        return magic;
+    }
+}

+ 15 - 2
src/main/java/org/elasticsearch/index/query/SimpleQueryStringParser.java

@@ -30,6 +30,7 @@ import org.elasticsearch.common.settings.Settings;
 import org.elasticsearch.common.xcontent.XContentParser;
 
 import java.io.IOException;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.Map;
 
@@ -86,6 +87,7 @@ public class SimpleQueryStringParser implements QueryParser {
         Map<String, Float> fieldsAndWeights = null;
         BooleanClause.Occur defaultOperator = null;
         Analyzer analyzer = null;
+        int flags = -1;
 
         XContentParser.Token token;
         while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
@@ -146,6 +148,17 @@ public class SimpleQueryStringParser implements QueryParser {
                         throw new QueryParsingException(parseContext.index(),
                                 "[" + NAME + "] default operator [" + op + "] is not allowed");
                     }
+                } else if ("flags".equals(currentFieldName)) {
+                    if (parser.hasTextCharacters()) {
+                        // Possible options are:
+                        // ALL, NONE, AND, OR, PREFIX, PHRASE, PRECEDENCE, ESCAPE, WHITESPACE
+                        flags = SimpleQueryStringFlag.resolveFlags(parser.text());
+                    } else {
+                        flags = parser.intValue();
+                        if (flags < 0) {
+                            flags = SimpleQueryStringFlag.ALL.value();
+                        }
+                    }
                 } else {
                     throw new QueryParsingException(parseContext.index(), "[" + NAME + "] unsupported field [" + parser.currentName() + "]");
                 }
@@ -174,9 +187,9 @@ public class SimpleQueryStringParser implements QueryParser {
 
         XSimpleQueryParser sqp;
         if (fieldsAndWeights != null) {
-            sqp = new XSimpleQueryParser(analyzer, fieldsAndWeights);
+            sqp = new XSimpleQueryParser(analyzer, fieldsAndWeights, flags);
         } else {
-            sqp = new XSimpleQueryParser(analyzer, field);
+            sqp = new XSimpleQueryParser(analyzer, Collections.singletonMap(field, 1.0F), flags);
         }
 
         if (defaultOperator != null) {

+ 55 - 0
src/test/java/org/elasticsearch/search/query/SimpleQueryTests.java

@@ -1955,4 +1955,59 @@ public class SimpleQueryTests extends ElasticsearchIntegrationTest {
         assertFirstHit(searchResponse, hasId("5"));
         assertSearchHits(searchResponse, "5", "6");
     }
+
+    @Test
+    public void testSimpleQueryStringFlags() {
+        assertAcked(client().admin().indices().prepareCreate("test").setSettings(SETTING_NUMBER_OF_SHARDS, 1));
+        client().prepareIndex("test", "type1", "1").setSource("body", "foo").get();
+        client().prepareIndex("test", "type1", "2").setSource("body", "bar").get();
+        client().prepareIndex("test", "type1", "3").setSource("body", "foo bar").get();
+        client().prepareIndex("test", "type1", "4").setSource("body", "quux baz eggplant").get();
+        client().prepareIndex("test", "type1", "5").setSource("body", "quux baz spaghetti").get();
+        client().prepareIndex("test", "type1", "6").setSource("otherbody", "spaghetti").get();
+        refresh();
+
+        SearchResponse searchResponse = client().prepareSearch().setQuery(
+                simpleQueryString("foo bar").flags(SimpleQueryStringFlag.ALL)).get();
+        assertHitCount(searchResponse, 3l);
+        assertSearchHits(searchResponse, "1", "2", "3");
+
+        searchResponse = client().prepareSearch().setQuery(
+                simpleQueryString("foo | bar")
+                        .defaultOperator(SimpleQueryStringBuilder.Operator.AND)
+                        .flags(SimpleQueryStringFlag.OR)).get();
+        assertHitCount(searchResponse, 3l);
+        assertSearchHits(searchResponse, "1", "2", "3");
+
+        searchResponse = client().prepareSearch().setQuery(
+                simpleQueryString("foo | bar")
+                        .defaultOperator(SimpleQueryStringBuilder.Operator.AND)
+                        .flags(SimpleQueryStringFlag.NONE)).get();
+        assertHitCount(searchResponse, 1l);
+        assertFirstHit(searchResponse, hasId("3"));
+
+        searchResponse = client().prepareSearch().setQuery(
+                simpleQueryString("baz | egg*")
+                        .defaultOperator(SimpleQueryStringBuilder.Operator.AND)
+                        .flags(SimpleQueryStringFlag.NONE)).get();
+        assertHitCount(searchResponse, 0l);
+
+        searchResponse = client().prepareSearch().setSource("{\n" +
+                "  \"query\": {\n" +
+                "    \"simple_query_string\": {\n" +
+                "      \"query\": \"foo|bar\",\n" +
+                "      \"default_operator\": \"AND\"," +
+                "      \"flags\": \"NONE\"\n" +
+                "    }\n" +
+                "  }\n" +
+                "}").get();
+        assertHitCount(searchResponse, 1l);
+
+        searchResponse = client().prepareSearch().setQuery(
+                simpleQueryString("baz | egg*")
+                        .defaultOperator(SimpleQueryStringBuilder.Operator.AND)
+                        .flags(SimpleQueryStringFlag.WHITESPACE, SimpleQueryStringFlag.PREFIX)).get();
+        assertHitCount(searchResponse, 1l);
+        assertFirstHit(searchResponse, hasId("4"));
+    }
 }