Require timeout units when parsing query body

Today when parsing the timeout field in a query body, if time units are
supplied the parser throws a NumberFormatException. Addtionally, the
parsing allows the timeout field to not specify units (it assumes
milliseconds). This commit fixes this behavior by not only allowing time
units to be specified but requires time units to be specified. This is
consistent with the documented behavior and the behavior in 2.x.

Relates #19077
This commit is contained in:
Jason Tedor 2016-06-25 16:18:25 -04:00 committed by GitHub
parent 09c0285d9c
commit c79e27180e
2 changed files with 27 additions and 4 deletions

View File

@ -21,7 +21,6 @@ package org.elasticsearch.search.builder;
import com.carrotsearch.hppc.ObjectFloatHashMap; import com.carrotsearch.hppc.ObjectFloatHashMap;
import com.carrotsearch.hppc.cursors.ObjectCursor; import com.carrotsearch.hppc.cursors.ObjectCursor;
import org.elasticsearch.action.support.ToXContentToBytes; import org.elasticsearch.action.support.ToXContentToBytes;
import org.elasticsearch.common.Nullable; import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.ParseField; import org.elasticsearch.common.ParseField;
@ -42,7 +41,6 @@ import org.elasticsearch.index.query.QueryParseContext;
import org.elasticsearch.index.query.QueryShardContext; import org.elasticsearch.index.query.QueryShardContext;
import org.elasticsearch.script.Script; import org.elasticsearch.script.Script;
import org.elasticsearch.search.aggregations.AggregationBuilder; import org.elasticsearch.search.aggregations.AggregationBuilder;
import org.elasticsearch.search.slice.SliceBuilder;
import org.elasticsearch.search.aggregations.AggregatorFactories; import org.elasticsearch.search.aggregations.AggregatorFactories;
import org.elasticsearch.search.aggregations.AggregatorParsers; import org.elasticsearch.search.aggregations.AggregatorParsers;
import org.elasticsearch.search.aggregations.PipelineAggregationBuilder; import org.elasticsearch.search.aggregations.PipelineAggregationBuilder;
@ -51,6 +49,7 @@ import org.elasticsearch.search.highlight.HighlightBuilder;
import org.elasticsearch.search.internal.SearchContext; import org.elasticsearch.search.internal.SearchContext;
import org.elasticsearch.search.rescore.RescoreBuilder; import org.elasticsearch.search.rescore.RescoreBuilder;
import org.elasticsearch.search.searchafter.SearchAfterBuilder; import org.elasticsearch.search.searchafter.SearchAfterBuilder;
import org.elasticsearch.search.slice.SliceBuilder;
import org.elasticsearch.search.sort.ScoreSortBuilder; import org.elasticsearch.search.sort.ScoreSortBuilder;
import org.elasticsearch.search.sort.SortBuilder; import org.elasticsearch.search.sort.SortBuilder;
import org.elasticsearch.search.sort.SortBuilders; import org.elasticsearch.search.sort.SortBuilders;
@ -959,7 +958,7 @@ public final class SearchSourceBuilder extends ToXContentToBytes implements Writ
} else if (context.getParseFieldMatcher().match(currentFieldName, SIZE_FIELD)) { } else if (context.getParseFieldMatcher().match(currentFieldName, SIZE_FIELD)) {
size = parser.intValue(); size = parser.intValue();
} else if (context.getParseFieldMatcher().match(currentFieldName, TIMEOUT_FIELD)) { } else if (context.getParseFieldMatcher().match(currentFieldName, TIMEOUT_FIELD)) {
timeoutInMillis = parser.longValue(); timeoutInMillis = TimeValue.parseTimeValue(parser.text(), null, TIMEOUT_FIELD.getPreferredName()).millis();
} else if (context.getParseFieldMatcher().match(currentFieldName, TERMINATE_AFTER_FIELD)) { } else if (context.getParseFieldMatcher().match(currentFieldName, TERMINATE_AFTER_FIELD)) {
terminateAfter = parser.intValue(); terminateAfter = parser.intValue();
} else if (context.getParseFieldMatcher().match(currentFieldName, MIN_SCORE_FIELD)) { } else if (context.getParseFieldMatcher().match(currentFieldName, MIN_SCORE_FIELD)) {
@ -1105,7 +1104,7 @@ public final class SearchSourceBuilder extends ToXContentToBytes implements Writ
} }
if (timeoutInMillis != -1) { if (timeoutInMillis != -1) {
builder.field(TIMEOUT_FIELD.getPreferredName(), timeoutInMillis); builder.field(TIMEOUT_FIELD.getPreferredName(), TimeValue.timeValueMillis(timeoutInMillis).toString());
} }
if (terminateAfter != SearchContext.DEFAULT_TERMINATE_AFTER) { if (terminateAfter != SearchContext.DEFAULT_TERMINATE_AFTER) {

View File

@ -19,6 +19,7 @@
package org.elasticsearch.search.builder; package org.elasticsearch.search.builder;
import org.elasticsearch.ElasticsearchParseException;
import org.elasticsearch.Version; import org.elasticsearch.Version;
import org.elasticsearch.cluster.ClusterState; import org.elasticsearch.cluster.ClusterState;
import org.elasticsearch.cluster.metadata.IndexMetaData; import org.elasticsearch.cluster.metadata.IndexMetaData;
@ -89,7 +90,9 @@ import java.util.concurrent.TimeUnit;
import static org.elasticsearch.test.ClusterServiceUtils.createClusterService; import static org.elasticsearch.test.ClusterServiceUtils.createClusterService;
import static org.elasticsearch.test.ClusterServiceUtils.setState; import static org.elasticsearch.test.ClusterServiceUtils.setState;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasToString;
public class SearchSourceBuilderTests extends ESTestCase { public class SearchSourceBuilderTests extends ESTestCase {
private static Injector injector; private static Injector injector;
@ -593,6 +596,27 @@ public class SearchSourceBuilderTests extends ESTestCase {
} }
} }
public void testTimeoutWithUnits() throws IOException {
final String timeout = randomTimeValue();
final String query = "{ \"query\": { \"match_all\": {}}, \"timeout\": \"" + timeout + "\"}";
try (XContentParser parser = XContentFactory.xContent(query).createParser(query)) {
final SearchSourceBuilder builder = SearchSourceBuilder.fromXContent(createParseContext(parser), aggParsers, suggesters);
assertThat(builder.timeoutInMillis(), equalTo(TimeValue.parseTimeValue(timeout, null, "timeout").millis()));
}
}
public void testTimeoutWithoutUnits() throws IOException {
final int timeout = randomIntBetween(1, 1024);
final String query = "{ \"query\": { \"match_all\": {}}, \"timeout\": \"" + timeout + "\"}";
try (XContentParser parser = XContentFactory.xContent(query).createParser(query)) {
final ElasticsearchParseException e =
expectThrows(
ElasticsearchParseException.class,
() -> SearchSourceBuilder.fromXContent(createParseContext(parser), aggParsers, suggesters));
assertThat(e, hasToString(containsString("unit is missing or unrecognized")));
}
}
public void testEmptyPostFilter() throws IOException { public void testEmptyPostFilter() throws IOException {
SearchSourceBuilder builder = new SearchSourceBuilder(); SearchSourceBuilder builder = new SearchSourceBuilder();
String query = "{ \"post_filter\": {} }"; String query = "{ \"post_filter\": {} }";