mirror of
https://github.com/honeymoose/OpenSearch.git
synced 2025-02-17 18:35:25 +00:00
SQL: Add all QUERY() query options (elastic/x-pack-elasticsearch#3389)
This adds support for (almost) all of the options that the `query_string` query supports. Additionally, it reverts back to the default operator of `OR` rather than `AND` for the `QUERY()` query. Relates to elastic/x-pack-elasticsearch#3361 Original commit: elastic/x-pack-elasticsearch@da8b29b53c
This commit is contained in:
parent
5b47c67dec
commit
c26f039207
@ -10,7 +10,14 @@ SELECT emp_no, first_name, gender, last_name FROM test_emp WHERE QUERY('Baek fox
|
||||
;
|
||||
|
||||
simpleQueryDedicatedField
|
||||
SELECT emp_no, first_name, gender, last_name FROM test_emp WHERE QUERY('Man*', 'fields=last_name') LIMIT 5;
|
||||
SELECT emp_no, first_name, gender, last_name FROM test_emp WHERE QUERY('Man*', 'default_field=last_name') LIMIT 5;
|
||||
|
||||
emp_no:i | first_name:s | gender:s | last_name:s
|
||||
10096 |Jayson |M |Mandell
|
||||
;
|
||||
|
||||
simpleQueryOptions
|
||||
SELECT emp_no, first_name, gender, last_name FROM test_emp WHERE QUERY('Man*', 'default_field=last_name;lenient=true;fuzzy_rewrite=scoring_boolean') LIMIT 5;
|
||||
|
||||
emp_no:i | first_name:s | gender:s | last_name:s
|
||||
10096 |Jayson |M |Mandell
|
||||
|
@ -14,21 +14,15 @@ import static java.util.Collections.emptyList;
|
||||
public class StringQueryPredicate extends FullTextPredicate {
|
||||
|
||||
private final Map<String, Float> fields;
|
||||
private final Operator defaultOperator;
|
||||
|
||||
public StringQueryPredicate(Location location, String query, String options) {
|
||||
super(location, query, options, emptyList());
|
||||
|
||||
// inferred
|
||||
this.fields = FullTextUtils.parseFields(optionMap(), location);
|
||||
this.defaultOperator = FullTextUtils.operator(optionMap(), "default_operator");
|
||||
}
|
||||
|
||||
public Map<String, Float> fields() {
|
||||
return fields;
|
||||
}
|
||||
|
||||
public Operator defaultOperator() {
|
||||
return defaultOperator;
|
||||
}
|
||||
}
|
||||
|
@ -24,7 +24,7 @@ public class MatchQuery extends LeafQuery {
|
||||
private static final Map<String, BiConsumer<MatchQueryBuilder, String>> BUILDER_APPLIERS;
|
||||
|
||||
static {
|
||||
HashMap<String, BiConsumer<MatchQueryBuilder, String>> appliers = new HashMap<>(14);
|
||||
HashMap<String, BiConsumer<MatchQueryBuilder, String>> appliers = new HashMap<>(11);
|
||||
// TODO: it'd be great if these could be constants instead of Strings, needs a core change to make the fields public first
|
||||
// TODO: add zero terms query support, I'm not sure the best way to parse it yet...
|
||||
// appliers.put("zero_terms_query", (qb, s) -> qb.zeroTermsQuery(s));
|
||||
|
@ -5,59 +5,102 @@
|
||||
*/
|
||||
package org.elasticsearch.xpack.sql.querydsl.query;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.elasticsearch.common.Booleans;
|
||||
import org.elasticsearch.index.query.MultiMatchQueryBuilder;
|
||||
import org.elasticsearch.index.query.Operator;
|
||||
import org.elasticsearch.index.query.QueryBuilder;
|
||||
import org.elasticsearch.index.query.QueryBuilders;
|
||||
import org.elasticsearch.index.query.QueryStringQueryBuilder;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.fulltext.StringQueryPredicate;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.fulltext.FullTextPredicate.Operator;
|
||||
import org.elasticsearch.xpack.sql.tree.Location;
|
||||
|
||||
import static java.util.Collections.singletonMap;
|
||||
|
||||
import static org.elasticsearch.index.query.QueryBuilders.queryStringQuery;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
public class QueryStringQuery extends LeafQuery {
|
||||
|
||||
private static final Map<String, BiConsumer<QueryStringQueryBuilder, String>> BUILDER_APPLIERS;
|
||||
|
||||
static {
|
||||
HashMap<String, BiConsumer<QueryStringQueryBuilder, String>> appliers = new HashMap<>(28);
|
||||
// TODO: it'd be great if these could be constants instead of Strings, needs a core change to make the fields public first
|
||||
appliers.put("default_field", (qb, s) -> qb.defaultField(s));
|
||||
appliers.put("default_operator", (qb, s) -> qb.defaultOperator(Operator.fromString(s)));
|
||||
appliers.put("analyzer", (qb, s) -> qb.analyzer(s));
|
||||
appliers.put("quote_analyzer", (qb, s) -> qb.quoteAnalyzer(s));
|
||||
appliers.put("allow_leading_wildcard", (qb, s) -> qb.allowLeadingWildcard(Booleans.parseBoolean(s)));
|
||||
appliers.put("auto_generate_phrase_queries", (qb, s) -> qb.autoGeneratePhraseQueries(Booleans.parseBoolean(s)));
|
||||
appliers.put("max_determinized_states", (qb, s) -> qb.maxDeterminizedStates(Integer.valueOf(s)));
|
||||
appliers.put("lowercase_expanded_terms", (qb, s) -> {});
|
||||
appliers.put("enable_position_increments", (qb, s) -> qb.enablePositionIncrements(Booleans.parseBoolean(s)));
|
||||
appliers.put("escape", (qb, s) -> qb.escape(Booleans.parseBoolean(s)));
|
||||
appliers.put("use_dis_max", (qb, s) -> qb.useDisMax(Booleans.parseBoolean(s)));
|
||||
appliers.put("fuzzy_prefix_length", (qb, s) -> qb.fuzzyPrefixLength(Integer.valueOf(s)));
|
||||
appliers.put("fuzzy_max_expansions", (qb, s) -> qb.fuzzyMaxExpansions(Integer.valueOf(s)));
|
||||
appliers.put("fuzzy_rewrite", (qb, s) -> qb.fuzzyRewrite(s));
|
||||
appliers.put("phrase_slop", (qb, s) -> qb.phraseSlop(Integer.valueOf(s)));
|
||||
appliers.put("tie_breaker", (qb, s) -> qb.tieBreaker(Float.valueOf(s)));
|
||||
appliers.put("analyze_wildcard", (qb, s) -> qb.analyzeWildcard(Booleans.parseBoolean(s)));
|
||||
appliers.put("rewrite", (qb, s) -> qb.rewrite(s));
|
||||
appliers.put("minimum_should_match", (qb, s) -> qb.minimumShouldMatch(s));
|
||||
appliers.put("quote_field_suffix", (qb, s) -> qb.quoteFieldSuffix(s));
|
||||
appliers.put("lenient", (qb, s) -> qb.lenient(Booleans.parseBoolean(s)));
|
||||
appliers.put("locale", (qb, s) -> {});
|
||||
appliers.put("time_zone", (qb, s) -> qb.timeZone(s));
|
||||
appliers.put("split_on_whitespace", (qb, s) -> qb.splitOnWhitespace(Booleans.parseBoolean(s)));
|
||||
appliers.put("all_fields", (qb, s) -> qb.useAllFields(Booleans.parseBoolean(s)));
|
||||
appliers.put("type", (qb, s) -> qb.type(MultiMatchQueryBuilder.Type.parse(s)));
|
||||
appliers.put("auto_generate_synonyms_phrase_query", (qb, s) -> qb.autoGenerateSynonymsPhraseQuery(Booleans.parseBoolean(s)));
|
||||
appliers.put("fuzzy_transpositions", (qb, s) -> qb.fuzzyTranspositions(Booleans.parseBoolean(s)));
|
||||
BUILDER_APPLIERS = Collections.unmodifiableMap(appliers);
|
||||
}
|
||||
|
||||
private final String query;
|
||||
private final Map<String, Float> fields;
|
||||
private Operator operator;
|
||||
private StringQueryPredicate predicate;
|
||||
private final Map<String, String> options;
|
||||
|
||||
// dedicated constructor for QueryTranslator
|
||||
public QueryStringQuery(Location location, String query, String fieldName) {
|
||||
this(location, query, singletonMap(fieldName, Float.valueOf(1.0f)), Operator.AND, null);
|
||||
this(location, query, Collections.singletonMap(fieldName, Float.valueOf(1.0f)), null);
|
||||
}
|
||||
|
||||
public QueryStringQuery(Location location, String query, Map<String, Float> fields, StringQueryPredicate predicate) {
|
||||
this(location, query, fields, null, predicate);
|
||||
}
|
||||
|
||||
private QueryStringQuery(Location location, String query, Map<String, Float> fields, Operator operator, StringQueryPredicate predicate) {
|
||||
super(location);
|
||||
this.query = query;
|
||||
this.fields = fields;
|
||||
this.predicate = predicate;
|
||||
this.operator = operator != null ? operator : predicate.defaultOperator();
|
||||
this.options = predicate == null ? Collections.emptyMap() : predicate.optionMap();
|
||||
}
|
||||
|
||||
@Override
|
||||
public QueryBuilder asBuilder() {
|
||||
QueryStringQueryBuilder queryBuilder = queryStringQuery(query);
|
||||
final QueryStringQueryBuilder queryBuilder = QueryBuilders.queryStringQuery(query);
|
||||
queryBuilder.fields(fields);
|
||||
if (operator != null) {
|
||||
queryBuilder.defaultOperator(operator.toEs());
|
||||
}
|
||||
if (predicate != null) {
|
||||
queryBuilder.analyzer(predicate.analyzer());
|
||||
}
|
||||
options.forEach((k, v) -> {
|
||||
if (BUILDER_APPLIERS.containsKey(k)) {
|
||||
BUILDER_APPLIERS.get(k).accept(queryBuilder, v);
|
||||
} else {
|
||||
throw new IllegalArgumentException("illegal query_string option [" + k + "]");
|
||||
}
|
||||
});
|
||||
return queryBuilder;
|
||||
}
|
||||
|
||||
public Map<String, Float> fields() {
|
||||
return fields;
|
||||
}
|
||||
|
||||
public String query() {
|
||||
return query;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(query, fields, operator, predicate);
|
||||
return Objects.hash(query, fields, predicate);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -65,15 +108,14 @@ public class QueryStringQuery extends LeafQuery {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
if (obj == null || getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
QueryStringQuery other = (QueryStringQuery) obj;
|
||||
return Objects.equals(query, other.query)
|
||||
&& Objects.equals(fields, other.fields)
|
||||
&& Objects.equals(operator, other.operator)
|
||||
&& Objects.equals(predicate, other.predicate);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
package org.elasticsearch.xpack.sql.querydsl.query;
|
||||
|
||||
import org.elasticsearch.ElasticsearchParseException;
|
||||
import org.elasticsearch.index.query.QueryStringQueryBuilder;
|
||||
import org.elasticsearch.index.query.Operator;
|
||||
import org.elasticsearch.test.ESTestCase;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.fulltext.StringQueryPredicate;
|
||||
import org.elasticsearch.xpack.sql.tree.Location;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
|
||||
public class QueryStringQueryTests extends ESTestCase {
|
||||
|
||||
public void testQueryBuilding() {
|
||||
QueryStringQueryBuilder qb = getBuilder("lenient=true");
|
||||
assertThat(qb.lenient(), equalTo(true));
|
||||
|
||||
qb = getBuilder("lenient=true;default_operator=AND");
|
||||
assertThat(qb.lenient(), equalTo(true));
|
||||
assertThat(qb.defaultOperator(), equalTo(Operator.AND));
|
||||
|
||||
Exception e = expectThrows(IllegalArgumentException.class, () -> getBuilder("pizza=yummy"));
|
||||
assertThat(e.getMessage(), equalTo("illegal query_string option [pizza]"));
|
||||
|
||||
e = expectThrows(ElasticsearchParseException.class, () -> getBuilder("type=aoeu"));
|
||||
assertThat(e.getMessage(), equalTo("failed to parse [multi_match] query type [aoeu]. unknown type."));
|
||||
}
|
||||
|
||||
private static QueryStringQueryBuilder getBuilder(String options) {
|
||||
final Location location = new Location(1, 1);
|
||||
final StringQueryPredicate mmqp = new StringQueryPredicate(location, "eggplant", options);
|
||||
final QueryStringQuery mmq = new QueryStringQuery(location, "eggplant", Collections.singletonMap("foo", 1.0f), mmqp);
|
||||
return (QueryStringQueryBuilder) mmq.asBuilder();
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user